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.source.jcr;
35
36 import info.magnolia.cms.util.ExceptionUtil;
37 import info.magnolia.config.NamedDefinition;
38 import info.magnolia.config.registry.DefinitionMetadata;
39 import info.magnolia.config.registry.DefinitionMetadataBuilder;
40 import info.magnolia.config.registry.DefinitionProvider;
41 import info.magnolia.config.registry.DefinitionProviderBuilder;
42 import info.magnolia.config.registry.DefinitionRawView;
43 import info.magnolia.config.registry.Registry;
44 import info.magnolia.config.source.ConfigurationSource;
45 import info.magnolia.config.source.ConfigurationSourceType;
46 import info.magnolia.config.source.ConfigurationSourceTypes;
47 import info.magnolia.jcr.node2bean.Node2BeanException;
48 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
49 import info.magnolia.jcr.node2bean.TransformationState;
50 import info.magnolia.jcr.node2bean.TypeDescriptor;
51 import info.magnolia.jcr.node2bean.TypeMapping;
52 import info.magnolia.jcr.node2bean.impl.Node2BeanTransformerImpl;
53 import info.magnolia.jcr.node2bean.impl.PreConfiguredBeanUtils;
54 import info.magnolia.jcr.predicate.NodeTypePredicate;
55 import info.magnolia.jcr.util.NodeTypes;
56 import info.magnolia.jcr.util.NodeUtil;
57 import info.magnolia.jcr.util.NodeVisitor;
58 import info.magnolia.module.ModuleRegistry;
59 import info.magnolia.objectfactory.ComponentProvider;
60 import info.magnolia.objectfactory.Components;
61
62 import java.io.IOException;
63 import java.util.ArrayList;
64 import java.util.HashMap;
65 import java.util.HashSet;
66 import java.util.Iterator;
67 import java.util.LinkedList;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Set;
71
72 import javax.jcr.Node;
73 import javax.jcr.RepositoryException;
74
75 import org.apache.commons.lang3.StringUtils;
76 import org.apache.jackrabbit.commons.predicate.Predicate;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80
81
82
83
84
85 public class JcrConfigurationSource<T> implements ConfigurationSource {
86
87 private static final Logger log = LoggerFactory.getLogger(JcrConfigurationSource.class);
88
89 private final Registry<T> registry;
90 private final String pathInModule;
91 private final Predicate nodeFilter;
92 private final Node2BeanProcessor node2BeanProcessor;
93 private Set<DefinitionMetadata> registeredIds = new HashSet<>();
94 private final RegistryBasedObservingManager observingManager;
95
96 public JcrConfigurationSource(Registry<T> registry, String pathInModule, Predicate nodeFilter,
97 Node2BeanProcessor node2BeanProcessor, ModuleRegistry moduleRegistry) throws IOException {
98 this.registry = registry;
99 this.pathInModule = pathInModule;
100 this.nodeFilter = nodeFilter;
101 this.node2BeanProcessor = node2BeanProcessor;
102
103 this.observingManager = new RegistryBasedObservingManager(pathInModule, moduleRegistry, this);
104 }
105
106 @Override
107 public ConfigurationSourceType type() {
108 return ConfigurationSourceTypes.jcr;
109 }
110
111 @Override
112 public void start() {
113 this.observingManager.start();
114 }
115
116 protected void reload(List<Node> nodes) throws RepositoryException {
117 final List<DefinitionProvider<T>> providers = new ArrayList<>();
118
119 for (Node node : nodes) {
120 collectProvidersFor(providers, node);
121 }
122
123 this.registeredIds = registry.unregisterAndRegister(registeredIds, providers);
124 }
125
126 protected void collectProvidersFor(final List<DefinitionProvider<T>> providers, Node node) throws RepositoryException {
127 NodeUtil.visit(node, new NodeVisitor() {
128 @Override
129 public void visit(Node parent) throws RepositoryException {
130 for (Node configNode : NodeUtil.getNodes(parent, NodeTypes.ContentNode.NAME)) {
131 if (checkNode(configNode)) {
132 DefinitionProvider<T> provider = newProvider(configNode);
133 if (provider != null) {
134 providers.add(provider);
135 }
136 }
137 }
138 }
139 }, new NodeTypePredicate(NodeTypes.Content.NAME, false));
140 }
141
142
143
144
145 protected boolean checkNode(Node node) {
146 return nodeFilter.evaluate(node);
147 }
148
149 protected DefinitionProvider<T> newProvider(Node configNode) throws RepositoryException {
150 String nodePath = configNode.getPath();
151
152 final DefinitionProviderBuilder<T> builder = DefinitionProviderBuilder.newBuilder();
153 final DefinitionMetadataBuilder metadataBuilder = createMetadata(configNode);
154 builder.metadata(metadataBuilder);
155
156 try {
157
158 MapPopulatingN2BTransformer transformer = new MapPopulatingN2BTransformer();
159 @SuppressWarnings("unchecked")
160 final T bean = (T) node2BeanProcessor.toBean(configNode, true, transformer, Components.getComponentProvider());
161 builder.definition(bean);
162
163
164 if (bean instanceof NamedDefinition) {
165 final String definitionName = ((NamedDefinition) bean).getName();
166 if (definitionName != null) {
167
168 metadataBuilder.name(definitionName);
169 }
170 }
171
172
173 builder.rawView(transformer.getRawView().getSubRawView());
174
175 } catch (Throwable t) {
176
177 builder.addErrorMessage(ExceptionUtil.exceptionToWords(t));
178 log.warn("Problem while registering {} from {}: {}", getRegistry().type(), nodePath, ExceptionUtil.exceptionToWords(t), t);
179 }
180
181 DefinitionProvider<T> provider = builder.build();
182 return provider;
183 }
184
185 protected DefinitionMetadataBuilder createMetadata(Node configNode) throws RepositoryException {
186 final String fallbackDefinitionName = configNode.getName();
187
188 final String path = configNode.getPath();
189 final String[] pathElements = path.split("/");
190 final String moduleName = pathElements[2];
191 final String relPath = StringUtils.removeStart(path, "/modules/" + moduleName + "/" + pathInModule + "/");
192
193 return registry.newMetadataBuilder()
194 .type(getRegistry().type())
195 .name(fallbackDefinitionName)
196 .module(moduleName)
197 .location(path)
198 .relativeLocation(relPath);
199 }
200
201 protected Registry<T> getRegistry() {
202 return registry;
203 }
204
205
206
207
208 private static class DefinitionRawViewImpl implements DefinitionRawView {
209
210 private List<Property> properties;
211
212 public DefinitionRawViewImpl(List<Property> properties) {
213 this.properties = properties;
214 }
215
216 @Override
217 public List<Property> properties() {
218 return properties;
219 }
220 }
221
222
223
224
225
226
227 private class MapPopulatingN2BTransformer extends Node2BeanTransformerImpl {
228
229 private Map<String, DefinitionRawView.Property> populatedProperties = new HashMap<>();
230
231 private DefinitionRawView.Property rawView;
232
233 public MapPopulatingN2BTransformer() {
234 super(new PreConfiguredBeanUtils());
235 }
236
237 @Override
238 protected TypeDescriptor onResolveType(TypeMapping mapping, TransformationState state, TypeDescriptor resolvedType, ComponentProvider componentProvider) {
239 if (resolvedType == null && state.getLevel() == 1) {
240 return mapping.getTypeDescriptor(getRegistry().type().baseClass());
241 }
242 return resolvedType;
243 }
244
245 @Override
246 public void initBean(TransformationState state, Map values) throws Node2BeanException {
247
248 super.initBean(state, values);
249
250 final List<DefinitionRawView.Property> properties = new LinkedList<>();
251 try {
252 final Iterator<Map.Entry<String, Object>> it = values.entrySet().iterator();
253
254 while (it.hasNext()) {
255 final Map.Entry<String, Object> entry = it.next();
256
257 if (this.populatedProperties.containsKey(entry.getKey())) {
258 properties.add(this.populatedProperties.remove(entry.getKey()));
259 } else {
260 properties.add(DefinitionRawView.Property.simple(entry.getKey(), String.valueOf(entry.getValue())));
261 }
262 }
263
264
265 if (state.getCurrentNode().hasProperty("class")) {
266 properties.add(DefinitionRawView.Property.simple("class", state.getCurrentNode().getProperty("class").getString()));
267 }
268
269 String name = state.getCurrentNode().getName();
270 if (state.getCurrentType().isCollection() || state.getCurrentType().isMap() || state.getCurrentType().isArray()) {
271 rawView = DefinitionRawView.Property.collection(name, properties);
272 } else {
273 rawView = DefinitionRawView.Property.subBean(name, new DefinitionRawViewImpl(properties));
274 }
275
276 this.populatedProperties.put(name, rawView);
277
278 } catch (RepositoryException e) {
279
280 }
281 }
282
283
284 public DefinitionRawView.Property getRawView() {
285 return rawView;
286 }
287 }
288 }