1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.objectfactory.guice;
35
36 import info.magnolia.module.model.ComponentDefinition;
37 import info.magnolia.objectfactory.ComponentFactory;
38 import info.magnolia.objectfactory.annotation.LazySingleton;
39 import info.magnolia.objectfactory.annotation.LocalScoped;
40 import info.magnolia.objectfactory.annotation.Multibinding;
41 import info.magnolia.objectfactory.annotation.SessionScoped;
42 import info.magnolia.objectfactory.configuration.ComponentConfiguration;
43 import info.magnolia.objectfactory.configuration.ComponentProviderConfiguration;
44 import info.magnolia.objectfactory.configuration.ConfiguredComponentConfiguration;
45 import info.magnolia.objectfactory.configuration.ImplementationConfiguration;
46 import info.magnolia.objectfactory.configuration.InstanceConfiguration;
47 import info.magnolia.objectfactory.configuration.ProviderConfiguration;
48
49 import java.lang.annotation.Annotation;
50 import java.util.Map;
51 import java.util.Set;
52
53 import javax.inject.Provider;
54 import javax.inject.Singleton;
55
56 import org.apache.commons.lang3.StringUtils;
57 import org.reflections.ReflectionUtils;
58
59 import com.google.inject.AbstractModule;
60 import com.google.inject.Key;
61 import com.google.inject.Module;
62 import com.google.inject.binder.ScopedBindingBuilder;
63 import com.google.inject.multibindings.Multibinder;
64 import com.google.inject.util.Providers;
65
66
67
68
69
70 public class GuiceComponentConfigurationModule extends AbstractModule {
71
72 private final ComponentProviderConfiguration configuration;
73
74 public GuiceComponentConfigurationModule(ComponentProviderConfiguration configuration) {
75 this.configuration = configuration;
76 }
77
78 @Override
79 protected void configure() {
80
81 for (Map.Entry<Class, ComponentConfiguration> entry : configuration.getComponents().entrySet()) {
82 bindConfiguration(entry.getValue());
83 }
84
85 for (Object configurer : configuration.getConfigurers()) {
86 if (configurer instanceof Module) {
87 install((Module) configurer);
88 }
89 }
90 }
91
92 private <T> void bindConfiguration(ComponentConfiguration<T> configuration) {
93 if (configuration instanceof ImplementationConfiguration) {
94 bindImplementation((ImplementationConfiguration<T>) configuration);
95 } else if (configuration instanceof InstanceConfiguration) {
96 bindInstance((InstanceConfiguration<T>) configuration);
97 } else if (configuration instanceof ProviderConfiguration) {
98 bindProvider((ProviderConfiguration<T>) configuration);
99 } else if (configuration instanceof ConfiguredComponentConfiguration) {
100 ConfiguredComponentConfiguration<T> configured = (ConfiguredComponentConfiguration<T>) configuration;
101 if (configured.isObserved()) {
102 bindObservedComponent(configured);
103 } else {
104 bindConfiguredComponent(configured);
105 }
106 } else {
107 throw new IllegalStateException("Component configuration is ambiguous for component with type [" + configuration.getType() + "]");
108 }
109 }
110
111 private <T> void bindConfiguredComponent(ConfiguredComponentConfiguration<T> configuration) {
112 Provider<T> provider = new GuiceConfiguredComponentProvider<T>(configuration.getWorkspace(), configuration.getPath());
113 ScopedBindingBuilder builder = bindProvider(configuration.getType(), provider);
114 bindInScope(builder, configuration);
115 }
116
117 private <T> void bindObservedComponent(ConfiguredComponentConfiguration<T> configuration) {
118 Class<T> key = configuration.getType();
119 Provider<T> provider = new GuiceObservedComponentProvider<T>(configuration.getWorkspace(), configuration.getPath(), key);
120 ScopedBindingBuilder builder = bindProvider(configuration.getType(), provider);
121 bindInScope(builder, configuration);
122 }
123
124 private <T> void bindProvider(ProviderConfiguration<T> configuration) {
125 Class<?> factoryClass = configuration.getProviderClass();
126
127 if (ComponentFactory.class.isAssignableFrom(factoryClass)) {
128 Provider<T> provider = GuiceUtils.providerForComponentFactory((Class<? extends ComponentFactory<T>>) factoryClass);
129 ScopedBindingBuilder builder = bindProvider(configuration.getType(), provider);
130 bindInScope(builder, configuration);
131 } else if (Provider.class.isAssignableFrom(factoryClass)) {
132 ScopedBindingBuilder builder = bindProvider(configuration.getType(), (Class<? extends Provider<T>>) factoryClass);
133 bindInScope(builder, configuration);
134 } else {
135 throw new IllegalStateException("Unsupported provider class [" + factoryClass + "] for component with type [" + configuration.getType() + "]");
136 }
137 }
138
139 private <T> void bindInstance(InstanceConfiguration<T> configuration) {
140 Class<T> key = configuration.getType();
141 Object instance = configuration.getInstance();
142 if (instance instanceof Provider) {
143 bindProvider(configuration.getType(), (Provider<T>) instance);
144 } else if (instance instanceof ComponentFactory) {
145 bindProvider(configuration.getType(), GuiceUtils.providerForComponentFactory((ComponentFactory<T>) instance));
146 } else {
147 bind(key).toInstance((T) instance);
148 }
149
150 }
151
152 private <T> void bindImplementation(ImplementationConfiguration<T> configuration) {
153 Class<T> key = configuration.getType();
154 Class<? extends T> implementation = configuration.getImplementation();
155
156 ScopedBindingBuilder builder;
157 if (key.equals(implementation)) {
158 builder = bind(implementation);
159 } else {
160 builder = bind(key).to(implementation);
161 }
162 bindInScope(builder, configuration);
163
164
165 final Set<Class<?>> multiBindingClasses = getAllSuperTypesWith(implementation, Multibinding.class);
166 for (Class multibindingClass : multiBindingClasses) {
167
168 addMultiBinding(key, multibindingClass);
169 }
170 }
171
172 private <T extends M, M> void addMultiBinding(Class<T> key, Class<M> multibindingClass) {
173
174
175
176
177
178
179
180 final Multibinder<M> multiBinder = Multibinder.newSetBinder(binder(), multibindingClass);
181 multiBinder.addBinding().to(Key.get(key));
182 }
183
184 @SuppressWarnings("unchecked")
185 private static Set<Class<?>> getAllSuperTypesWith(Class<?> type, Class<? extends Annotation> annotationClass) {
186 return ReflectionUtils.getAllSuperTypes(type, ReflectionUtils.withAnnotation(annotationClass));
187 }
188
189 private <T> ScopedBindingBuilder bindProvider(Class<T> type, Provider<T> provider) {
190 return bind(type).toProvider(Providers.guicify(provider));
191 }
192
193 private <T> ScopedBindingBuilder bindProvider(Class<T> type, Class<? extends Provider<T>> provider) {
194 return bind(type).toProvider(provider);
195 }
196
197 private <T> void bindInScope(ScopedBindingBuilder builder, ComponentConfiguration<T> configuration) {
198 Class<? extends Annotation> scopeAnnotation = getScope(configuration);
199 if (scopeAnnotation != null) {
200 builder.in(scopeAnnotation);
201 }
202 }
203
204 private Class<? extends Annotation> getScope(ComponentConfiguration<?> componentConfiguration) {
205 String scope = componentConfiguration.getScope();
206 if (StringUtils.isEmpty(scope)) {
207 return null;
208 }
209 if (ComponentDefinition.SCOPE_SINGLETON.equalsIgnoreCase(scope) && !componentConfiguration.isLazy()) {
210 return Singleton.class;
211 }
212 if (ComponentDefinition.SCOPE_SINGLETON.equalsIgnoreCase(scope) && componentConfiguration.isLazy()) {
213 return LazySingleton.class;
214 }
215 if (ComponentDefinition.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
216 return LocalScoped.class;
217 }
218 if (ComponentDefinition.SCOPE_SESSION.equalsIgnoreCase(scope)) {
219 return SessionScoped.class;
220 }
221 throw new IllegalStateException("Unknown scope [" + scope + "] for component with type [" + componentConfiguration.getType() + "]");
222 }
223 }