alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (ThrowingProviderBinder.java)

This example Java source code file (ThrowingProviderBinder.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

annotation, checkedprovider, class, exception, key, list, object, override, reflection, result, resultexception, scopedbindingbuilder, secondarybinder, throwable, typeliteral, util

The ThrowingProviderBinder.java Java example source code

/**
 * Copyright (C) 2007 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.throwingproviders;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ProviderWithDependencies;
import com.google.inject.util.Types;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * <p>Builds a binding for a {@link CheckedProvider}.
 * 
 * <p>You can use a fluent API and custom providers:
 * <pre>ThrowingProviderBinder.create(binder())
 *    .bind(RemoteProvider.class, Customer.class)
 *    .to(RemoteCustomerProvider.class)
 *    .in(RequestScope.class);
 * </code>
* or, you can use throwing provider methods: * <pre>class MyModule extends AbstractModule { * configure() { * ThrowingProviderBinder.install(this, binder()); * } * * {@literal @}CheckedProvides(RemoteProvider.class) * {@literal @}RequestScope * Customer provideCustomer(FlakyCustomerCreator creator) throws RemoteException { * return creator.getCustomerOrThrow(); * } * } * </code> * You also can declare that a CheckedProvider construct * a particular class whose constructor throws an exception: * <pre>ThrowingProviderBinder.create(binder()) * .bind(RemoteProvider.class, Customer.class) * .providing(CustomerImpl.class) * .in(RequestScope.class); * </code> * * @author jmourits@google.com (Jerome Mourits) * @author jessewilson@google.com (Jesse Wilson) * @author sameb@google.com (Sam Berlin) */ public class ThrowingProviderBinder { private static final TypeLiteral<CheckedProvider CHECKED_PROVIDER_TYPE = new TypeLiteral<CheckedProvider() { }; private static final TypeLiteral<CheckedProviderMethod CHECKED_PROVIDER_METHOD_TYPE = new TypeLiteral<CheckedProviderMethod() { }; private final Binder binder; private ThrowingProviderBinder(Binder binder) { this.binder = binder; } public static ThrowingProviderBinder create(Binder binder) { return new ThrowingProviderBinder(binder.skipSources( ThrowingProviderBinder.class, ThrowingProviderBinder.SecondaryBinder.class)); } /** * Returns a module that installs {@literal @}{@link CheckedProvides} methods. * * @since 3.0 */ public static Module forModule(Module module) { return CheckedProviderMethodsModule.forModule(module); } /** * @deprecated Use {@link #bind(Class, Class)} or {@link #bind(Class, TypeLiteral)} instead. */ @Deprecated public <P extends CheckedProvider> SecondaryBinder bind(Class<P> interfaceType, Type clazz) { return new SecondaryBinder<P, Object>(interfaceType, clazz); } /** * @since 4.0 */ public <P extends CheckedProvider, T> SecondaryBinder bind(Class<P> interfaceType, Class clazz) { return new SecondaryBinder<P, T>(interfaceType, clazz); } /** * @since 4.0 */ public <P extends CheckedProvider, T> SecondaryBinder bind(Class<P> interfaceType, TypeLiteral typeLiteral) { return new SecondaryBinder<P, T>(interfaceType, typeLiteral.getType()); } public class SecondaryBinder<P extends CheckedProvider, T> { private final Class<P> interfaceType; private final Type valueType; private final List<Class exceptionTypes; private final boolean valid; private Class<? extends Annotation> annotationType; private Annotation annotation; private Key<P> interfaceKey; private boolean scopeExceptions = true; public SecondaryBinder(Class<P> interfaceType, Type valueType) { this.interfaceType = checkNotNull(interfaceType, "interfaceType"); this.valueType = checkNotNull(valueType, "valueType"); if(checkInterface()) { this.exceptionTypes = getExceptionType(interfaceType); valid = true; } else { valid = false; this.exceptionTypes = ImmutableList.of(); } } List<Class getExceptionTypes() { return exceptionTypes; } Key<P> getKey() { return interfaceKey; } public SecondaryBinder<P, T> annotatedWith(Class annotationType) { if (!(this.annotationType == null && this.annotation == null)) { throw new IllegalStateException("Cannot set annotation twice"); } this.annotationType = annotationType; return this; } public SecondaryBinder<P, T> annotatedWith(Annotation annotation) { if (!(this.annotationType == null && this.annotation == null)) { throw new IllegalStateException("Cannot set annotation twice"); } this.annotation = annotation; return this; } /** * Determines if exceptions should be scoped. By default exceptions are scoped. * * @param scopeExceptions whether exceptions should be scoped. * @since 4.0 */ public SecondaryBinder<P, T> scopeExceptions(boolean scopeExceptions) { this.scopeExceptions = scopeExceptions; return this; } public ScopedBindingBuilder to(P target) { Key<P> targetKey = Key.get(interfaceType, UniqueAnnotations.create()); binder.bind(targetKey).toInstance(target); return to(targetKey); } public ScopedBindingBuilder to(Class<? extends P> targetType) { return to(Key.get(targetType)); } /** @since 4.0 */ public ScopedBindingBuilder providing(Class<? extends T> cxtorClass) { return providing(TypeLiteral.get(cxtorClass)); } /** @since 4.0 */ @SuppressWarnings("unchecked") // safe because this is the cxtor of the literal public ScopedBindingBuilder providing(TypeLiteral<? extends T> cxtorLiteral) { // Find a constructor that has @ThrowingInject. Constructor<? extends T> cxtor = CheckedProvideUtils.findThrowingConstructor(cxtorLiteral, binder); final Provider<T> typeProvider; final Key<? extends T> typeKey; // If we found an injection point, then bind the cxtor to a unique key if (cxtor != null) { // Validate the exceptions are consistent with the CheckedProvider interface. CheckedProvideUtils.validateExceptions( binder, cxtorLiteral.getExceptionTypes(cxtor), exceptionTypes, interfaceType); typeKey = Key.get(cxtorLiteral, UniqueAnnotations.create()); binder.bind(typeKey).toConstructor((Constructor) cxtor).in(Scopes.NO_SCOPE); typeProvider = binder.getProvider((Key<T>) typeKey); } else { // never used, but need it assigned. typeProvider = null; typeKey = null; } // Create a CheckedProvider that calls our cxtor CheckedProvider<T> checkedProvider = new CheckedProviderWithDependencies() { @Override public T get() throws Exception { try { return typeProvider.get(); } catch (ProvisionException pe) { // Rethrow the provision cause as the actual exception if (pe.getCause() instanceof Exception) { throw (Exception) pe.getCause(); } else if (pe.getCause() instanceof Error) { throw (Error) pe.getCause(); } else { // If this failed because of multiple reasons (ie, more than // one dependency failed due to scoping errors), then // the ProvisionException won't have a cause, so we need // to rethrow it as-is. throw pe; } } } @Override public Set<Dependency getDependencies() { return ImmutableSet.<Dependencyof(Dependency.get(typeKey)); } }; Key<CheckedProvider targetKey = Key.get(CHECKED_PROVIDER_TYPE, UniqueAnnotations.create()); binder.bind(targetKey).toInstance(checkedProvider); return toInternal(targetKey); } ScopedBindingBuilder toProviderMethod(CheckedProviderMethod<?> target) { Key<CheckedProviderMethod targetKey = Key.get(CHECKED_PROVIDER_METHOD_TYPE, UniqueAnnotations.create()); binder.bind(targetKey).toInstance(target); return toInternal(targetKey); } @SuppressWarnings("unchecked") // P only extends the raw type of CheckedProvider public ScopedBindingBuilder to(Key<? extends P> targetKey) { checkNotNull(targetKey, "targetKey"); return toInternal((Key<? extends CheckedProvider)targetKey); } private ScopedBindingBuilder toInternal(final Key<? extends CheckedProvider targetKey) { final Key<Result> resultKey = Key.get(Result.class, UniqueAnnotations.create()); // Note that this provider will behave like the final provider Guice creates. // It will especially do scoping if the user adds that. final Provider<Result> resultProvider = binder.getProvider(resultKey); final Provider<? extends CheckedProvider targetProvider = binder.getProvider(targetKey); interfaceKey = createKey(); // don't bother binding the proxy type if this is in an invalid state. if(valid) { binder.bind(interfaceKey).toProvider(new ProviderWithDependencies<P>() { private final P instance = interfaceType.cast(Proxy.newProxyInstance( interfaceType.getClassLoader(), new Class<?>[] { interfaceType }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Allow methods like .equals(..), .hashcode(..), .toString(..) to work. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (scopeExceptions) { return resultProvider.get().getOrThrow(); } else { Result result; try { result = resultProvider.get(); } catch (ProvisionException pe) { Throwable cause = pe.getCause(); if (cause instanceof ResultException) { throw ((ResultException)cause).getCause(); } else { throw pe; } } return result.getOrThrow(); } } })); @Override public P get() { return instance; } @Override public Set<Dependency getDependencies() { return ImmutableSet.<Dependencyof(Dependency.get(resultKey)); } }); } // The provider is unscoped, but the user may apply a scope to it through the // ScopedBindingBuilder this returns. return binder.bind(resultKey).toProvider( createResultProvider(targetKey, targetProvider)); } private ProviderWithDependencies<Result> createResultProvider( final Key<? extends CheckedProvider targetKey, final Provider<? extends CheckedProvider targetProvider) { return new ProviderWithDependencies<Result>() { @Override public Result get() { try { return Result.forValue(targetProvider.get().get()); } catch (Exception e) { for (Class<? extends Throwable> exceptionType : exceptionTypes) { if (exceptionType.isInstance(e)) { if (scopeExceptions) { return Result.forException(e); } else { throw new ResultException(e); } } } if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { // this should never happen throw new RuntimeException(e); } } } @Override public Set<Dependency getDependencies() { return ImmutableSet.<Dependencyof(Dependency.get(targetKey)); } }; } /** * Returns the exception type declared to be thrown by the get method of * {@code interfaceType}. */ private List<Class getExceptionType(Class

interfaceType) { try { Method getMethod = interfaceType.getMethod("get"); List<TypeLiteral exceptionLiterals = TypeLiteral.get(interfaceType).getExceptionTypes(getMethod); List<Class results = Lists.newArrayList(); for (TypeLiteral<?> exLiteral : exceptionLiterals) { results.add(exLiteral.getRawType().asSubclass(Throwable.class)); } return results; } catch (SecurityException e) { throw new IllegalStateException("Not allowed to inspect exception types", e); } catch (NoSuchMethodException e) { throw new IllegalStateException("No 'get'method available", e); } } private boolean checkInterface() { if(!checkArgument(interfaceType.isInterface(), "%s must be an interface", interfaceType.getName())) { return false; } if(!checkArgument(interfaceType.getGenericInterfaces().length == 1, "%s must extend CheckedProvider (and only CheckedProvider)", interfaceType)) { return false; } boolean tpMode = interfaceType.getInterfaces()[0] == ThrowingProvider.class; if(!tpMode) { if(!checkArgument(interfaceType.getInterfaces()[0] == CheckedProvider.class, "%s must extend CheckedProvider (and only CheckedProvider)", interfaceType)) { return false; } } // Ensure that T is parameterized and unconstrained. ParameterizedType genericThrowingProvider = (ParameterizedType) interfaceType.getGenericInterfaces()[0]; if (interfaceType.getTypeParameters().length == 1) { String returnTypeName = interfaceType.getTypeParameters()[0].getName(); Type returnType = genericThrowingProvider.getActualTypeArguments()[0]; if(!checkArgument(returnType instanceof TypeVariable, "%s does not properly extend CheckedProvider, the first type parameter of CheckedProvider (%s) is not a generic type", interfaceType, returnType)) { return false; } if(!checkArgument(returnTypeName.equals(((TypeVariable) returnType).getName()), "The generic type (%s) of %s does not match the generic type of CheckedProvider (%s)", returnTypeName, interfaceType, ((TypeVariable)returnType).getName())) { return false; } } else { if(!checkArgument(interfaceType.getTypeParameters().length == 0, "%s has more than one generic type parameter: %s", interfaceType, Arrays.asList(interfaceType.getTypeParameters()))) { return false; } if(!checkArgument(genericThrowingProvider.getActualTypeArguments()[0].equals(valueType), "%s expects the value type to be %s, but it was %s", interfaceType, genericThrowingProvider.getActualTypeArguments()[0], valueType)) { return false; } } if(tpMode) { // only validate exception in ThrowingProvider mode. Type exceptionType = genericThrowingProvider.getActualTypeArguments()[1]; if(!checkArgument(exceptionType instanceof Class, "%s has the wrong Exception generic type (%s) when extending CheckedProvider", interfaceType, exceptionType)) { return false; } } // Skip synthetic/bridge methods because java8 generates // a default method on the interface w/ the superinterface type that // just delegates directly to the overridden method. List<Method> declaredMethods = FluentIterable .from(Arrays.asList(interfaceType.getDeclaredMethods())) .filter(NotSyntheticOrBridgePredicate.INSTANCE) .toList(); if (declaredMethods.size() == 1) { Method method = declaredMethods.get(0); if(!checkArgument(method.getName().equals("get"), "%s may not declare any new methods, but declared %s", interfaceType, method)) { return false; } if(!checkArgument(method.getParameterTypes().length == 0, "%s may not declare any new methods, but declared %s", interfaceType, method.toGenericString())) { return false; } } else { if(!checkArgument(declaredMethods.isEmpty(), "%s may not declare any new methods, but declared %s", interfaceType, Arrays.asList(interfaceType.getDeclaredMethods()))) { return false; } } return true; } private boolean checkArgument(boolean condition, String messageFormat, Object... args) { if (!condition) { binder.addError(messageFormat, args); return false; } else { return true; } } @SuppressWarnings({"unchecked"}) private Key<P> createKey() { TypeLiteral<P> typeLiteral; if (interfaceType.getTypeParameters().length == 1) { ParameterizedType type = Types.newParameterizedTypeWithOwner( interfaceType.getEnclosingClass(), interfaceType, valueType); typeLiteral = (TypeLiteral<P>) TypeLiteral.get(type); } else { typeLiteral = TypeLiteral.get(interfaceType); } if (annotation != null) { return Key.get(typeLiteral, annotation); } else if (annotationType != null) { return Key.get(typeLiteral, annotationType); } else { return Key.get(typeLiteral); } } } /** * Represents the returned value from a call to {@link CheckedProvider#get()}. This is the value * that will be scoped by Guice. */ static class Result implements Serializable { private static final long serialVersionUID = 0L; private final Object value; private final Exception exception; private Result(Object value, Exception exception) { this.value = value; this.exception = exception; } public static Result forValue(Object value) { return new Result(value, null); } public static Result forException(Exception e) { return new Result(null, e); } public Object getOrThrow() throws Exception { if (exception != null) { throw exception; } else { return value; } } } /** * RuntimeException class to wrap exceptions from the checked provider. * The regular guice provider can throw it and the checked provider proxy extracts * the underlying exception and rethrows it. */ private static class ResultException extends RuntimeException { ResultException(Exception cause) { super(cause); } } private static class NotSyntheticOrBridgePredicate implements Predicate<Method> { static NotSyntheticOrBridgePredicate INSTANCE = new NotSyntheticOrBridgePredicate(); @Override public boolean apply(Method input) { return !input.isBridge() && !input.isSynthetic(); } } }

Other Java examples (source code examples)

Here is a short list of links related to this Java ThrowingProviderBinder.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.