001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.guice; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Field; 021import java.lang.reflect.Modifier; 022import java.util.concurrent.ConcurrentMap; 023 024import javax.inject.Qualifier; 025 026import org.apache.wicket.injection.IFieldValueFactory; 027import org.apache.wicket.proxy.LazyInitProxyFactory; 028 029import com.google.inject.BindingAnnotation; 030import com.google.inject.Inject; 031import org.apache.wicket.util.lang.Generics; 032 033/** 034 * 035 */ 036public class GuiceFieldValueFactory implements IFieldValueFactory 037{ 038 private final ConcurrentMap<GuiceProxyTargetLocator, Object> cache = Generics.newConcurrentHashMap(); 039 private static final Object NULL_SENTINEL = new Object(); 040 041 private final boolean wrapInProxies; 042 043 /** 044 * Construct. 045 * 046 * @param wrapInProxies 047 */ 048 GuiceFieldValueFactory(final boolean wrapInProxies) 049 { 050 this.wrapInProxies = wrapInProxies; 051 } 052 053 /** 054 * {@inheritDoc} 055 */ 056 @Override 057 public Object getFieldValue(final Field field, final Object fieldOwner) 058 { 059 Object target = null; 060 061 if (supportsField(field)) 062 { 063 Inject injectAnnotation = field.getAnnotation(Inject.class); 064 javax.inject.Inject javaxInjectAnnotation = field.getAnnotation(javax.inject.Inject.class); 065 if (!Modifier.isStatic(field.getModifiers()) && (injectAnnotation != null || javaxInjectAnnotation != null)) 066 { 067 try 068 { 069 boolean optional = injectAnnotation != null && injectAnnotation.optional(); 070 Annotation bindingAnnotation = findBindingAnnotation(field.getAnnotations()); 071 final GuiceProxyTargetLocator locator = new GuiceProxyTargetLocator(field, bindingAnnotation, optional); 072 073 Object cachedValue = cache.get(locator); 074 if (cachedValue != null) 075 { 076 return cachedValue == NULL_SENTINEL ? null : cachedValue; 077 } 078 079 target = locator.locateProxyTarget(); 080 if (target == null) 081 { 082 // Optional without a binding, return null 083 } 084 else 085 { 086 if (wrapInProxies) 087 { 088 target = LazyInitProxyFactory.createProxy(field.getType(), locator); 089 } 090 } 091 092 if (locator.isSingletonScope()) 093 { 094 Object tmpTarget = cache.putIfAbsent(locator, target == null ? NULL_SENTINEL : target); 095 if (tmpTarget != null) 096 { 097 target = tmpTarget; 098 } 099 } 100 101 if (!field.canAccess(fieldOwner)) 102 { 103 field.setAccessible(true); 104 } 105 } 106 catch (MoreThanOneBindingException e) 107 { 108 throw new RuntimeException( 109 "Can't have more than one BindingAnnotation on field " + field.getName() + 110 " of class " + fieldOwner.getClass().getName()); 111 } 112 } 113 } 114 115 return target == NULL_SENTINEL ? null : target; 116 } 117 118 /** 119 * {@inheritDoc} 120 */ 121 @Override 122 public boolean supportsField(final Field field) 123 { 124 return field.isAnnotationPresent(Inject.class) || field.isAnnotationPresent(javax.inject.Inject.class); 125 } 126 127 /** 128 * 129 * @param annotations 130 * @return Annotation 131 * @throws MoreThanOneBindingException 132 */ 133 private Annotation findBindingAnnotation(final Annotation[] annotations) 134 throws MoreThanOneBindingException 135 { 136 Annotation bindingAnnotation = null; 137 138 // Work out if we have a BindingAnnotation on this parameter. 139 for (Annotation annotation : annotations) 140 { 141 if (annotation.annotationType().getAnnotation(BindingAnnotation.class) != null || 142 annotation.annotationType().getAnnotation(Qualifier.class) != null) 143 { 144 if (bindingAnnotation != null) 145 { 146 throw new MoreThanOneBindingException(); 147 } 148 bindingAnnotation = annotation; 149 } 150 } 151 return bindingAnnotation; 152 } 153 154 /** 155 * 156 */ 157 public static class MoreThanOneBindingException extends Exception 158 { 159 private static final long serialVersionUID = 1L; 160 } 161}