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.config.module;
35
36 import static info.magnolia.transformer.TransformationProblem.warning;
37
38 import info.magnolia.context.MgnlContext;
39 import info.magnolia.event.EventBus;
40 import info.magnolia.event.SystemEventBus;
41 import info.magnolia.jcr.node2bean.PropertyTypeDescriptor;
42 import info.magnolia.jcr.node2bean.TypeDescriptor;
43 import info.magnolia.jcr.node2bean.TypeMapping;
44 import info.magnolia.jcr.node2bean.impl.PreConfiguredBeanUtils;
45 import info.magnolia.map2bean.Map2BeanTransformer;
46 import info.magnolia.map2bean.TransformationState;
47 import info.magnolia.module.ModuleLifecycle;
48 import info.magnolia.module.ModuleRegistry;
49 import info.magnolia.module.StartModuleEvent;
50 import info.magnolia.module.StopModuleEvent;
51 import info.magnolia.module.model.ModuleDefinition;
52 import info.magnolia.objectfactory.ComponentProvider;
53 import info.magnolia.transformer.BeanTypeResolver;
54 import info.magnolia.transformer.TransformationResult;
55
56 import java.lang.reflect.InvocationTargetException;
57 import java.lang.reflect.Method;
58 import java.util.ArrayList;
59 import java.util.Collection;
60 import java.util.Map;
61 import java.util.Optional;
62 import java.util.stream.Collectors;
63 import java.util.stream.Stream;
64
65 import javax.inject.Inject;
66 import javax.inject.Named;
67
68 import org.apache.commons.beanutils.PropertyUtils;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72
73
74
75
76 class ModuleMap2BeanTransformer extends Map2BeanTransformer {
77
78 private static final Logger log = LoggerFactory.getLogger(ModuleMap2BeanTransformer.class);
79
80 private final ModuleRegistry moduleRegistry;
81 private final EventBus eventBus;
82 private final Map<String, String> moduleClassToModuleName;
83
84 @Inject
85 ModuleMap2BeanTransformer(ComponentProvider componentProvider, TypeMapping mapping, PreConfiguredBeanUtils beanUtils, BeanTypeResolver beanTypeResolver,
86 ModuleRegistry moduleRegistry, @Named(SystemEventBus.NAME) EventBus eventBus) {
87 super(componentProvider, mapping, beanUtils, beanTypeResolver);
88 this.moduleRegistry = moduleRegistry;
89 this.eventBus = eventBus;
90 this.moduleClassToModuleName = moduleRegistry.getModuleDefinitions().stream()
91 .filter(moduleDefinition -> moduleDefinition.getClassName() != null)
92 .collect(Collectors.toMap(ModuleDefinition::getClassName, ModuleDefinition::getName));
93 }
94
95 @Override
96 public <T> TransformationResult<T> transform(Map<String, Object> map, Class<T> targetType) {
97 return MgnlContext.doInSystemContext(() -> {
98 if (ModuleLifecycle.class.isAssignableFrom(targetType)) {
99 eventBus.fireEvent(new StopModuleEvent(moduleClassToModuleName.get(targetType.getName())));
100 TransformationResult<T> result = super.transform(map, targetType);
101 eventBus.fireEvent(new StartModuleEvent(moduleClassToModuleName.get(targetType.getName())));
102 return result;
103 } else {
104 return super.transform(map, targetType);
105 }
106 });
107 }
108
109 @Override
110 protected Object createInstance(Class<?> type) {
111 Object instance;
112 if (moduleClassToModuleName.containsKey(type.getName())) {
113 instance = moduleRegistry.getModuleInstance(type);
114 } else {
115 instance = super.createInstance(type);
116 }
117 return instance;
118 }
119
120 @Override
121 protected void handleMissingProperty(TransformationState state, Object bean, String sourcePropertyName, Object sourcePropertyValue, String missingPropertyMessage) {
122 Optional<Method> addMethodOptional = Stream.of(bean.getClass().getMethods())
123 .filter(method -> method.getParameterCount() == 1 && method.getName().startsWith("add"))
124 .findFirst();
125
126
127
128
129
130 if (addMethodOptional.isPresent()) {
131 Method addMethod = addMethodOptional.get();
132 PropertyTypeDescriptor propertyTypeDescriptor = new PropertyTypeDescriptor();
133 TypeDescriptor type = new TypeDescriptor();
134 Class<?> parameterType = addMethod.getParameterTypes()[0];
135 type.setType(parameterType);
136 propertyTypeDescriptor.setType(type);
137 state.pushEntry(sourcePropertyName, sourcePropertyValue, propertyTypeDescriptor);
138 try {
139 Object value = readValue(state);
140 if (parameterType.isInstance(value)) {
141 addMethod.invoke(bean, value);
142 return;
143 }
144 } catch (Exception e) {
145 state.trackProblem(warning(missingPropertyMessage));
146 } finally {
147 state.popCurrentEntry();
148 }
149 }
150 super.handleMissingProperty(state, bean, sourcePropertyName, sourcePropertyValue, missingPropertyMessage);
151 }
152
153 @Override
154 protected Collection<Object> readCollection(TransformationState state) {
155 Collection<Object> collection = super.readCollection(state);
156 new ArrayList<>(collection).stream()
157 .filter(o -> !isBeanEnabled(o))
158 .forEach(collection::remove);
159 return collection;
160 }
161
162 @Override
163 protected Map<String, Object> readMap(TransformationState state) {
164 Map<String, Object> map = super.readMap(state);
165 new ArrayList<>(map.keySet()).stream()
166 .filter(o -> !isBeanEnabled(map.get(o)))
167 .forEach(map::remove);
168 return map;
169 }
170
171
172
173
174 private Boolean isBeanEnabled(Object bean) {
175 try {
176 Boolean enabled = (Boolean) PropertyUtils.getSimpleProperty(bean, "enabled");
177
178
179
180 return enabled == null? true : enabled;
181 } catch (NoSuchMethodException | IllegalArgumentException e) {
182
183 return true;
184 } catch (IllegalAccessException e) {
185 log.warn("Can't access method [{}#isEnabled]. Maybe it's private/protected?", bean.getClass());
186 return true;
187 } catch (InvocationTargetException e) {
188 log.error("An exception was thrown by [{}#isEnabled] method.", bean.getClass(), e);
189 return true;
190 }
191 }
192 }