/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.wicket.guice; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Type; import com.google.inject.ConfigurationException; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; import org.apache.wicket.Application; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.proxy.IProxyTargetLocator; import org.apache.wicket.core.util.lang.WicketObjects; import org.apache.wicket.util.lang.Objects; class GuiceProxyTargetLocator implements IProxyTargetLocator { private static final long serialVersionUID = 1L; private final Annotation bindingAnnotation; private final boolean optional; private final String className; private final String fieldName; private Boolean isSingletonCache = null; public GuiceProxyTargetLocator(final Field field, final Annotation bindingAnnotation, final boolean optional) { this.bindingAnnotation = bindingAnnotation; this.optional = optional; className = field.getDeclaringClass().getName(); fieldName = field.getName(); } @Override public Object locateProxyTarget() { Injector injector = getInjector(); final Key<?> key = newGuiceKey(); // if the Inject annotation is marked optional and no binding is found // then skip this injection (WICKET-2241) if (optional) { // Guice 2.0 throws a ConfigurationException if no binding is find while 1.0 simply // returns null. try { if (injector.getBinding(key) == null) { return null; } } catch (RuntimeException e) { return null; } } return injector.getInstance(key); } private Key<?> newGuiceKey() { final Type type; try { Class<?> clazz = WicketObjects.resolveClass(className); final Field field = clazz.getDeclaredField(fieldName); type = field.getGenericType(); } catch (Exception e) { throw new WicketRuntimeException("Error accessing member: " + fieldName + " of class: " + className, e); } // using TypeLiteral to retrieve the key gives us automatic support for // Providers and other injectable TypeLiterals if (bindingAnnotation == null) { return Key.get(TypeLiteral.get(type)); } else { return Key.get(TypeLiteral.get(type), bindingAnnotation); } } public boolean isSingletonScope() { if (isSingletonCache == null) { try { isSingletonCache = Scopes.isSingleton(getInjector().getBinding(newGuiceKey())); } catch (ConfigurationException ex) { // No binding, if optional can pretend this is null singleton if (optional) isSingletonCache = true; else throw ex; } } return isSingletonCache; } private Injector getInjector() { final GuiceInjectorHolder holder = Application.get().getMetaData( GuiceInjectorHolder.INJECTOR_KEY); return holder.getInjector(); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof GuiceProxyTargetLocator)) return false; GuiceProxyTargetLocator that = (GuiceProxyTargetLocator) o; return Objects.equal(optional, that.optional) && Objects.equal(bindingAnnotation, that.bindingAnnotation) && Objects.equal(className, that.className) && Objects.equal(fieldName, that.fieldName); } @Override public int hashCode() { return Objects.hashCode(bindingAnnotation, optional, className, fieldName); } }