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