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.config.NamedDefinition;
37 import info.magnolia.config.registry.DefinitionMetadata;
38 import info.magnolia.config.registry.DefinitionMetadataBuilder;
39 import info.magnolia.config.registry.DefinitionProvider;
40 import info.magnolia.config.registry.DefinitionProviderBuilder;
41 import info.magnolia.config.registry.DefinitionProviderProblemLogger;
42 import info.magnolia.config.registry.Registry;
43 import info.magnolia.config.source.ConfigurationSource;
44 import info.magnolia.config.source.ConfigurationSourceType;
45 import info.magnolia.config.source.ConfigurationSourceTypes;
46 import info.magnolia.config.source.raw.DefinitionRawViewMapWrapper;
47 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
48 import info.magnolia.jcr.node2bean.impl.Node2BeanTransformerImpl;
49 import info.magnolia.jcr.node2bean.impl.PreConfiguredBeanUtils;
50 import info.magnolia.jcr.predicate.NodeTypePredicate;
51 import info.magnolia.jcr.util.NodeTypes;
52 import info.magnolia.jcr.util.NodeUtil;
53 import info.magnolia.jcr.util.NodeVisitor;
54 import info.magnolia.jcr.util.PropertyUtil;
55 import info.magnolia.jcr.wrapper.ExtendingNodeWrapper;
56 import info.magnolia.map2bean.Map2BeanTransformer;
57 import info.magnolia.module.ModuleRegistry;
58 import info.magnolia.objectfactory.Components;
59 import info.magnolia.transformer.TransformationResult;
60
61 import java.io.IOException;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.HashSet;
65 import java.util.LinkedHashMap;
66 import java.util.List;
67 import java.util.Map;
68 import java.util.Set;
69 import java.util.stream.Collectors;
70
71 import javax.inject.Inject;
72 import javax.jcr.Node;
73 import javax.jcr.NodeIterator;
74 import javax.jcr.Property;
75 import javax.jcr.PropertyIterator;
76 import javax.jcr.RepositoryException;
77
78 import org.apache.commons.lang3.StringUtils;
79 import org.apache.jackrabbit.commons.predicate.Predicate;
80 import org.slf4j.Logger;
81 import org.slf4j.LoggerFactory;
82
83
84
85
86
87
88 public class JcrConfigurationSource<T> implements ConfigurationSource {
89
90 private static final Logger log = LoggerFactory.getLogger(JcrConfigurationSource.class);
91 private static final String NAME_PROPERTY = "name";
92
93 private final Registry<T> registry;
94 private final String pathInModule;
95 private final Predicate nodeFilter;
96 private final Map2BeanTransformer map2BeanTransformer;
97 private Set<DefinitionMetadata> registeredIds = new HashSet<>();
98 private final RegistryBasedObservingManager observingManager;
99
100 @Inject
101 public JcrConfigurationSource(Registry<T> registry, String pathInModule, Predicate nodeFilter,
102 ModuleRegistry moduleRegistry, Map2BeanTransformer map2BeanTransformer) {
103 this.registry = registry;
104 this.pathInModule = pathInModule;
105 this.nodeFilter = nodeFilter;
106 this.map2BeanTransformer = map2BeanTransformer;
107
108 this.observingManager = getObservingManager(pathInModule, moduleRegistry);
109 }
110
111 protected RegistryBasedObservingManager getObservingManager(String pathInModule, ModuleRegistry moduleRegistry) {
112 return new RegistryBasedObservingManager(pathInModule, moduleRegistry, this);
113 }
114
115
116
117
118 @Deprecated
119 public JcrConfigurationSource(Registry<T> registry, String pathInModule, Predicate nodeFilter, Node2BeanProcessor node2BeanProcessor, ModuleRegistry moduleRegistry) throws IOException {
120 this(registry, pathInModule, nodeFilter, moduleRegistry, Components.getComponent(Map2BeanTransformer.class));
121 }
122
123 @Override
124 public ConfigurationSourceType type() {
125 return ConfigurationSourceTypes.jcr;
126 }
127
128 @Override
129 public void start() {
130 this.observingManager.start();
131 }
132
133 protected void reload(List<Node> nodes) throws RepositoryException {
134 final List<DefinitionProvider<T>> providers = new ArrayList<>();
135
136 for (Node node : nodes) {
137 collectProvidersFor(providers, node);
138 }
139
140 this.registeredIds = registry.unregisterAndRegister(registeredIds, providers);
141 }
142
143 protected void collectProvidersFor(final List<DefinitionProvider<T>> providers, Node node) throws RepositoryException {
144 NodeUtil.visit(node, new NodeVisitor() {
145 @Override
146 public void visit(Node parent) throws RepositoryException {
147 for (Node configNode : NodeUtil.getNodes(parent, NodeTypes.ContentNode.NAME)) {
148 if (checkNode(configNode)) {
149 DefinitionProvider<T> provider = newProvider(configNode);
150 if (provider != null) {
151 providers.add(provider);
152 }
153 }
154 }
155 }
156 }, new NodeTypePredicate(NodeTypes.Content.NAME, false));
157 }
158
159
160
161
162 protected boolean checkNode(Node node) {
163 return nodeFilter.evaluate(node);
164 }
165
166 protected DefinitionProvider<T> newProvider(Node configNode) throws RepositoryException {
167 final DefinitionProviderBuilder<T> builder = registry.newDefinitionProviderBuilder();
168 final DefinitionMetadataBuilder metadataBuilder = createMetadata(configNode);
169 builder.metadata(metadataBuilder);
170
171 final Node2MapTransformer transformer = new Node2MapTransformer();
172
173 final Map<String, Object> configMap = transformer.toMap(new ExtendingNodeWrapper(configNode));
174 final TransformationResult<T> transformationResult;
175
176 Class definitionType = metadataBuilder.getType().baseClass();
177
178 builder.rawView(new DefinitionRawViewMapWrapper(configMap));
179
180 if (NamedDefinition.class.isAssignableFrom(definitionType)) {
181 final Object nameValue = configMap.get(NAME_PROPERTY);
182 if (nameValue instanceof String) {
183 metadataBuilder.name((String) nameValue);
184 } else if (nameValue == null) {
185 configMap.put(NAME_PROPERTY, metadataBuilder.getName());
186 }
187 }
188
189 transformationResult = map2BeanTransformer.transform(configMap, definitionType);
190
191 builder.metadata(metadataBuilder);
192
193 final DefinitionProvider<T> definitionProvider = builder.buildFromTransformationResult(transformationResult);
194
195 DefinitionProviderProblemLogger
196 .withLoggingContext(log, true)
197 .logProblems(definitionProvider);
198
199 return definitionProvider;
200 }
201
202 protected DefinitionMetadataBuilder createMetadata(Node configNode) throws RepositoryException {
203 final String fallbackDefinitionName = configNode.getName();
204
205 final String path = configNode.getPath();
206 final String[] pathElements = path.split("/");
207 final String moduleName = pathElements[2];
208 final String relPath = StringUtils.removeStart(path, "/modules/" + moduleName + "/" + pathInModule + "/");
209
210 return registry.newMetadataBuilder()
211 .type(getRegistry().type())
212 .name(fallbackDefinitionName)
213 .module(moduleName)
214 .location(path)
215 .configurationSourceType(ConfigurationSourceTypes.jcr)
216 .relativeLocation(relPath);
217 }
218
219 protected Registry<T> getRegistry() {
220 return registry;
221 }
222
223
224
225
226 final static class Node2MapTransformer extends Node2BeanTransformerImpl {
227
228 Node2MapTransformer() {
229 super(new PreConfiguredBeanUtils());
230 }
231
232 Map<String, Object> toMap(Node node) throws RepositoryException {
233
234 Map<String, Object> map = new LinkedHashMap<>();
235 PropertyIterator it = getProperties(node);
236 while (it.hasNext()) {
237 Property p = it.nextProperty();
238 Object val = p.isMultiple() ? Arrays.stream(p.getValues()).map(PropertyUtil::getValueObject).collect(Collectors.toList()) : PropertyUtil.getValueObject(p.getValue());
239 if (val != null) {
240 map.put(p.getName(), val);
241 }
242 }
243 final NodeIterator children = getChildren(node);
244
245 while (children.hasNext()) {
246 Node childNode = (Node) children.next();
247
248
249
250 Object childBean = toMap(childNode);
251
252
253 if (childBean != null) {
254 String name = childNode.getName();
255 try {
256 if (childNode.getIndex() > 1) {
257 name += childNode.getIndex();
258 }
259 } catch (RepositoryException e) {
260 log.error("can't read index of the node [{}]", childNode, e);
261 }
262 map.put(name, childBean);
263 }
264 }
265
266 return map;
267 }
268 }
269 }