1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package org.apache.shiro.guice; |
20 | |
|
21 | |
import java.beans.PropertyDescriptor; |
22 | |
import java.lang.reflect.InvocationTargetException; |
23 | |
import java.lang.reflect.Modifier; |
24 | |
import java.lang.reflect.Type; |
25 | |
import java.util.Arrays; |
26 | |
import java.util.HashMap; |
27 | |
import java.util.HashSet; |
28 | |
import java.util.Map; |
29 | |
import java.util.Set; |
30 | |
|
31 | |
import com.google.inject.Binder; |
32 | |
import com.google.inject.ConfigurationException; |
33 | |
import com.google.inject.Injector; |
34 | |
import com.google.inject.Key; |
35 | |
import com.google.inject.MembersInjector; |
36 | |
import com.google.inject.Provider; |
37 | |
import com.google.inject.TypeLiteral; |
38 | |
import com.google.inject.matcher.Matcher; |
39 | |
import com.google.inject.matcher.Matchers; |
40 | |
import com.google.inject.multibindings.MapBinder; |
41 | |
import com.google.inject.name.Names; |
42 | |
import com.google.inject.spi.TypeEncounter; |
43 | |
import com.google.inject.spi.TypeListener; |
44 | |
import com.google.inject.util.Types; |
45 | |
|
46 | |
import org.apache.commons.beanutils.PropertyUtils; |
47 | |
import org.apache.shiro.SecurityUtils; |
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | 170 | class BeanTypeListener implements TypeListener { |
53 | 1 | public static final Package SHIRO_GUICE_PACKAGE = ShiroModule.class.getPackage(); |
54 | 1 | public static final Package SHIRO_PACKAGE = SecurityUtils.class.getPackage(); |
55 | |
|
56 | 1 | private static Matcher<Class> shiroMatcher = Matchers.inSubpackage(SHIRO_PACKAGE.getName()); |
57 | 1 | private static Matcher<Class> shiroGuiceMatcher = Matchers.inSubpackage(SHIRO_GUICE_PACKAGE.getName()); |
58 | |
|
59 | 1 | private static Matcher<Class> classMatcher = ShiroMatchers.ANY_PACKAGE.and(shiroMatcher.and(Matchers.not(shiroGuiceMatcher))); |
60 | |
|
61 | 1 | public static final Matcher<TypeLiteral> MATCHER = ShiroMatchers.typeLiteral(classMatcher); |
62 | |
|
63 | |
private static final String BEAN_TYPE_MAP_NAME = "__SHIRO_BEAN_TYPES__"; |
64 | 1 | static final Key<?> MAP_KEY = Key.get(Types.mapOf(TypeLiteral.class, BeanTypeKey.class), Names.named(BEAN_TYPE_MAP_NAME)); |
65 | |
|
66 | 1 | private static final Set<Class<?>> WRAPPER_TYPES = new HashSet<Class<?>>(Arrays.asList( |
67 | |
Byte.class, |
68 | |
Boolean.class, |
69 | |
Character.class, |
70 | |
Double.class, |
71 | |
Float.class, |
72 | |
Integer.class, |
73 | |
Long.class, |
74 | |
Short.class, |
75 | |
Void.class)); |
76 | |
|
77 | |
public <I> void hear(TypeLiteral<I> type, final TypeEncounter<I> encounter) { |
78 | 14 | PropertyDescriptor propertyDescriptors[] = PropertyUtils.getPropertyDescriptors(type.getRawType()); |
79 | 14 | final Map<PropertyDescriptor, Key<?>> propertyDependencies = new HashMap<PropertyDescriptor, Key<?>>(propertyDescriptors.length); |
80 | 14 | final Provider<Injector> injectorProvider = encounter.getProvider(Injector.class); |
81 | 150 | for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { |
82 | 136 | if (propertyDescriptor.getWriteMethod() != null && Modifier.isPublic(propertyDescriptor.getWriteMethod().getModifiers())) { |
83 | 118 | Type propertyType = propertyDescriptor.getWriteMethod().getGenericParameterTypes()[0]; |
84 | 118 | propertyDependencies.put(propertyDescriptor, createDependencyKey(propertyDescriptor, propertyType)); |
85 | |
} |
86 | |
} |
87 | 14 | encounter.register(new MembersInjector<I>() { |
88 | |
public void injectMembers(I instance) { |
89 | 18 | for (Map.Entry<PropertyDescriptor, Key<?>> dependency : propertyDependencies.entrySet()) { |
90 | |
try { |
91 | 158 | final Injector injector = injectorProvider.get(); |
92 | |
|
93 | 158 | Object value = injector.getInstance(getMappedKey(injector, dependency.getValue())); |
94 | 32 | dependency.getKey().getWriteMethod().invoke(instance, value); |
95 | |
|
96 | 126 | } catch (ConfigurationException e) { |
97 | |
|
98 | |
|
99 | 0 | } catch (InvocationTargetException e) { |
100 | 0 | throw new RuntimeException("Couldn't set property " + dependency.getKey().getDisplayName(), e); |
101 | 0 | } catch (IllegalAccessException e) { |
102 | 0 | throw new RuntimeException("We shouldn't have ever reached this point, we don't try to inject to non-accessible methods.", e); |
103 | 158 | } |
104 | 158 | } |
105 | |
|
106 | 18 | } |
107 | |
}); |
108 | 14 | } |
109 | |
|
110 | |
private static Key<?> getMappedKey(Injector injector, Key<?> key) { |
111 | 158 | Map<TypeLiteral, BeanTypeKey> beanTypeMap = getBeanTypeMap(injector); |
112 | 158 | if(key.getAnnotation() == null && beanTypeMap.containsKey(key.getTypeLiteral())) { |
113 | 4 | return beanTypeMap.get(key.getTypeLiteral()).key; |
114 | |
} else { |
115 | 154 | return key; |
116 | |
} |
117 | |
} |
118 | |
|
119 | |
@SuppressWarnings({"unchecked"}) |
120 | |
private static Map<TypeLiteral, BeanTypeKey> getBeanTypeMap(Injector injector) { |
121 | 158 | return (Map<TypeLiteral, BeanTypeKey>) injector.getInstance(MAP_KEY); |
122 | |
} |
123 | |
|
124 | |
private static Key<?> createDependencyKey(PropertyDescriptor propertyDescriptor, Type propertyType) { |
125 | 118 | if(requiresName(propertyType)) { |
126 | 28 | return Key.get(propertyType, Names.named("shiro." + propertyDescriptor.getName())); |
127 | |
} else { |
128 | 90 | return Key.get(propertyType); |
129 | |
} |
130 | |
} |
131 | |
|
132 | |
private static boolean requiresName(Type propertyType) { |
133 | 118 | if (propertyType instanceof Class) { |
134 | 109 | Class<?> aClass = (Class<?>) propertyType; |
135 | 109 | return aClass.isPrimitive() || aClass.isEnum() || WRAPPER_TYPES.contains(aClass) || CharSequence.class.isAssignableFrom(aClass); |
136 | |
} else { |
137 | 9 | return false; |
138 | |
} |
139 | |
} |
140 | |
|
141 | |
static void ensureBeanTypeMapExists(Binder binder) { |
142 | 11 | beanTypeMapBinding(binder).addBinding(TypeLiteral.get(BeanTypeKey.class)).toInstance(new BeanTypeKey(null)); |
143 | 11 | } |
144 | |
|
145 | |
static <T> void bindBeanType(Binder binder, TypeLiteral<T> typeLiteral, Key<? extends T> key) { |
146 | 5 | beanTypeMapBinding(binder).addBinding(typeLiteral).toInstance(new BeanTypeKey(key)); |
147 | 5 | } |
148 | |
|
149 | |
private static MapBinder<TypeLiteral, BeanTypeKey> beanTypeMapBinding(Binder binder) { |
150 | 16 | return MapBinder.newMapBinder(binder, TypeLiteral.class, BeanTypeKey.class, Names.named(BEAN_TYPE_MAP_NAME)); |
151 | |
} |
152 | |
|
153 | 16 | private static class BeanTypeKey { |
154 | |
Key<?> key; |
155 | |
|
156 | 16 | private BeanTypeKey(Key<?> key) { |
157 | 16 | this.key = key; |
158 | 16 | } |
159 | |
} |
160 | |
} |