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 = new RegistryBasedObservingManager(pathInModule, moduleRegistry, this);
109 }
110
111
112
113
114 @Deprecated
115 public JcrConfigurationSource(Registry<T> registry, String pathInModule, Predicate nodeFilter, Node2BeanProcessor node2BeanProcessor, ModuleRegistry moduleRegistry) throws IOException {
116 this(registry, pathInModule, nodeFilter, moduleRegistry, Components.getComponent(Map2BeanTransformer.class));
117 }
118
119 @Override
120 public ConfigurationSourceType type() {
121 return ConfigurationSourceTypes.jcr;
122 }
123
124 @Override
125 public void start() {
126 this.observingManager.start();
127 }
128
129 protected void reload(List<Node> nodes) throws RepositoryException {
130 final List<DefinitionProvider<T>> providers = new ArrayList<>();
131
132 for (Node node : nodes) {
133 collectProvidersFor(providers, node);
134 }
135
136 this.registeredIds = registry.unregisterAndRegister(registeredIds, providers);
137 }
138
139 protected void collectProvidersFor(final List<DefinitionProvider<T>> providers, Node node) throws RepositoryException {
140 NodeUtil.visit(node, new NodeVisitor() {
141 @Override
142 public void visit(Node parent) throws RepositoryException {
143 for (Node configNode : NodeUtil.getNodes(parent, NodeTypes.ContentNode.NAME)) {
144 if (checkNode(configNode)) {
145 DefinitionProvider<T> provider = newProvider(configNode);
146 if (provider != null) {
147 providers.add(provider);
148 }
149 }
150 }
151 }
152 }, new NodeTypePredicate(NodeTypes.Content.NAME, false));
153 }
154
155
156
157
158 protected boolean checkNode(Node node) {
159 return nodeFilter.evaluate(node);
160 }
161
162 protected DefinitionProvider<T> newProvider(Node configNode) throws RepositoryException {
163 final DefinitionProviderBuilder<T> builder = registry.newDefinitionProviderBuilder();
164 final DefinitionMetadataBuilder metadataBuilder = createMetadata(configNode);
165
166 final Node2MapTransformer transformer = new Node2MapTransformer();
167
168 final Map<String, Object> configMap = transformer.toMap(new ExtendingNodeWrapper(configNode));
169 final TransformationResult<T> transformationResult;
170
171 builder.rawView(new DefinitionRawViewMapWrapper(configMap));
172
173 if (NamedDefinition.class.isAssignableFrom(getRegistry().type().baseClass())) {
174 final Object nameValue = configMap.get(NAME_PROPERTY);
175 if (nameValue instanceof String) {
176 metadataBuilder.name((String) nameValue);
177 } else if (nameValue == null) {
178 configMap.put(NAME_PROPERTY, metadataBuilder.getName());
179 }
180 }
181
182 transformationResult = map2BeanTransformer.transform(configMap, getRegistry().type().baseClass());
183
184 builder.metadata(metadataBuilder);
185
186 final DefinitionProvider<T> definitionProvider = builder.buildFromTransformationResult(transformationResult);
187
188 DefinitionProviderProblemLogger
189 .withLoggingContext(log, true)
190 .logProblems(definitionProvider);
191
192 return definitionProvider;
193 }
194
195 protected DefinitionMetadataBuilder createMetadata(Node configNode) throws RepositoryException {
196 final String fallbackDefinitionName = configNode.getName();
197
198 final String path = configNode.getPath();
199 final String[] pathElements = path.split("/");
200 final String moduleName = pathElements[2];
201 final String relPath = StringUtils.removeStart(path, "/modules/" + moduleName + "/" + pathInModule + "/");
202
203 return registry.newMetadataBuilder()
204 .type(getRegistry().type())
205 .name(fallbackDefinitionName)
206 .module(moduleName)
207 .location(path)
208 .configurationSourceType(ConfigurationSourceTypes.jcr)
209 .relativeLocation(relPath);
210 }
211
212 protected Registry<T> getRegistry() {
213 return registry;
214 }
215
216
217
218
219 final static class Node2MapTransformer extends Node2BeanTransformerImpl {
220
221 Node2MapTransformer() {
222 super(new PreConfiguredBeanUtils());
223 }
224
225 Map<String, Object> toMap(Node node) throws RepositoryException {
226
227 Map<String, Object> map = new LinkedHashMap<>();
228 PropertyIterator it = getProperties(node);
229 while (it.hasNext()) {
230 Property p = it.nextProperty();
231 Object val = p.isMultiple() ? Arrays.stream(p.getValues()).map(PropertyUtil::getValueObject).collect(Collectors.toList()) : PropertyUtil.getValueObject(p.getValue());
232 if (val != null) {
233 map.put(p.getName(), val);
234 }
235 }
236 final NodeIterator children = getChildren(node);
237
238 while (children.hasNext()) {
239 Node childNode = (Node) children.next();
240
241
242
243 Object childBean = toMap(childNode);
244
245
246 if (childBean != null) {
247 String name = childNode.getName();
248 try {
249 if (childNode.getIndex() > 1) {
250 name += childNode.getIndex();
251 }
252 } catch (RepositoryException e) {
253 log.error("can't read index of the node [{}]", childNode, e);
254 }
255 map.put(name, childBean);
256 }
257 }
258
259 return map;
260 }
261 }
262 }