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.yaml;
35
36 import static info.magnolia.resourceloader.util.Functions.pathMatches;
37 import static info.magnolia.resourceloader.util.PredicatedResourceVisitor.onAllMatchingFiles;
38
39 import info.magnolia.config.registry.DefinitionMetadata;
40 import info.magnolia.config.registry.DefinitionMetadataBuilder;
41 import info.magnolia.config.registry.DefinitionProvider;
42 import info.magnolia.config.registry.Registry;
43 import info.magnolia.config.registry.RegistryTypeNameUtil;
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.config.source.yaml.decoration.FileDefinitionDecorator;
48 import info.magnolia.config.source.yaml.decoration.FileDefinitionDecoratorResolver;
49 import info.magnolia.resourceloader.Resource;
50 import info.magnolia.resourceloader.ResourceChangeHandler;
51 import info.magnolia.resourceloader.ResourceOrigin;
52 import info.magnolia.resourceloader.ResourceOriginChange;
53 import info.magnolia.resourceloader.ResourceVisitor;
54 import info.magnolia.resourceloader.util.PredicatedResourceVisitor;
55 import info.magnolia.resourceloader.util.VoidFunction;
56
57 import java.util.Collection;
58 import java.util.Collections;
59 import java.util.Iterator;
60 import java.util.Map;
61 import java.util.Map.Entry;
62 import java.util.Set;
63 import java.util.regex.Pattern;
64
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 import com.google.common.base.Optional;
69 import com.google.common.base.Predicate;
70 import com.google.common.collect.Collections2;
71 import com.google.common.collect.Maps;
72 import com.google.common.collect.Sets;
73
74
75
76
77
78
79 public abstract class AbstractFileResourceConfigurationSource<T> implements ConfigurationSource {
80
81 private static final Logger log = LoggerFactory.getLogger(AbstractFileResourceConfigurationSource.class);
82
83
84
85
86 private static final String PATH_PREFIX_TEMPLATE = "^%s($|/.+)";
87
88 private final ResourceOrigin<?> origin;
89 private final Pattern pathPattern;
90 private final Registry<T> registry;
91 private final PathToMetadataInferrer pathToMetadataInferrer;
92 private final Set<FileDefinitionDecoratorResolver> definitionDecoratorResolvers = Sets.newHashSet();
93 private final Map<String, FileDefinitionDecorator<T>> resolvedDefinitionDecorators = Maps.newHashMap();
94
95
96
97
98 public AbstractFileResourceConfigurationSource(ResourceOrigin<?> origin, Registry<T> registry, Pattern pathPattern) {
99 this.origin = origin;
100 this.registry = registry;
101 this.pathPattern = pathPattern;
102 this.pathToMetadataInferrer = new RegexBasedPathToMetadataInferrer(this.pathPattern);
103 }
104
105 @Override
106 public ConfigurationSourceType type() {
107 return ConfigurationSourceTypes.file;
108 }
109
110 @Override
111 public void start() {
112 log.info("Setting up {} to load {} definitions from resources", getClass().getSimpleName(), getRootType().getSimpleName());
113 final LoadAndRegisterFunction loadAndRegisterFunction = new LoadAndRegisterFunction();
114 final ResourceVisitor definitionProviderCollector = onAllMatchingFiles(pathMatches(pathPattern), loadAndRegisterFunction);
115
116 origin.traverseWith(definitionProviderCollector);
117 origin.registerResourceChangeHandler(new ResourceChangeHandler() {
118 @Override
119 public void onResourceChanged(ResourceOriginChange change) {
120 switch (change.getType()) {
121 case MODIFIED:
122 case ADDED:
123
124
125
126
127 if (pathPattern.matcher(change.getRelatedResourcePath()).matches()) {
128 loadAndRegister(origin.getByPath(change.getRelatedResourcePath()));
129 }
130 break;
131 case REMOVED:
132
133
134
135
136 removeDefinitionsMatchingPath(change.getRelatedResourcePath());
137 break;
138 }
139 }
140 });
141
142 startDecoration();
143 }
144
145 protected void startDecoration() {
146 final VoidFunction<Resource> definitionDecoratorResolutionFunction = new DefinitionDecoratorResolutionFunction();
147 final ResourceVisitor definitionDecoratorResolver = PredicatedResourceVisitor.with(definitionDecoratorResolutionFunction);
148
149 origin.traverseWith(definitionDecoratorResolver);
150 origin.registerResourceChangeHandler(new ResourceChangeHandler() {
151 @Override
152 public void onResourceChanged(ResourceOriginChange change) {
153 switch (change.getType()) {
154 case MODIFIED:
155 case ADDED:
156 final Resource resource = origin.getByPath(change.getRelatedResourcePath());
157 definitionDecoratorResolutionFunction.apply(resource);
158 break;
159 case REMOVED:
160 removeDefinitionDecoratorsMatchingPath(change.getRelatedResourcePath());
161 break;
162 }
163 }
164 });
165 }
166
167 private void removeDefinitionDecoratorsMatchingPath(String removedResourcePath) {
168
169 final Pattern removedPathPattern = Pattern.compile(String.format(PATH_PREFIX_TEMPLATE, removedResourcePath));
170
171 final Iterator<Entry<String,FileDefinitionDecorator<T>>> it = resolvedDefinitionDecorators.entrySet().iterator();
172 while (it.hasNext()) {
173 final Entry<String, FileDefinitionDecorator<T>> decoratorMapping = it.next();
174 if (removedPathPattern.matcher(decoratorMapping.getKey()).matches()) {
175 registry.removeDecorator(decoratorMapping.getValue());
176 it.remove();
177 }
178 }
179 }
180
181 protected final void removeDefinitionsMatchingPath(String removedResourcePath) {
182
183 final Pattern removedPathPattern = Pattern.compile(String.format(PATH_PREFIX_TEMPLATE, removedResourcePath));
184
185
186
187
188
189
190
191
192 final Collection<DefinitionMetadata> idsToRemove = Collections2.filter(getRegistry().getAllMetadata(), new Predicate<DefinitionMetadata>() {
193 @Override
194 public boolean apply(DefinitionMetadata definitionMetadata) {
195 return removedPathPattern.matcher(definitionMetadata.getLocation()).matches();
196 }
197 });
198
199 if (!idsToRemove.isEmpty()) {
200 getRegistry().unregisterAndRegister(idsToRemove, Collections.<DefinitionProvider<T>>emptySet());
201 }
202 }
203
204 public abstract void loadAndRegister(Resource resource);
205
206 protected Registry<T> getRegistry() {
207 return registry;
208 }
209
210 protected final Class<T> getRootType() {
211 return registry.type().baseClass();
212 }
213
214 protected DefinitionMetadataBuilder createMetadata(Resource resource) {
215 final DefinitionMetadataBuilder metadataBuilder = registry.newMetadataBuilder()
216 .type(getRegistry().type())
217 .location(resource.getPath());
218 return pathToMetadataInferrer.populateFrom(metadataBuilder, resource);
219 }
220
221
222 protected final void registerDefinitionDecoratorResolver(FileDefinitionDecoratorResolver resolver) {
223 definitionDecoratorResolvers.add(resolver);
224 }
225
226
227
228
229 protected final Map<String, FileDefinitionDecorator<T>> getResolvedDefinitionDecorators() {
230 return resolvedDefinitionDecorators;
231 }
232
233 private <T> Optional<FileDefinitionDecorator<T>> resolveDecorator(Resource resource) {
234 for (final FileDefinitionDecoratorResolver resolver : definitionDecoratorResolvers) {
235 final Optional<FileDefinitionDecorator<T>> resolutionResult = resolver.resolve(resource);
236 if (resolutionResult.isPresent()) {
237 return resolutionResult;
238 }
239 }
240
241
242
243 resolvedDefinitionDecorators.remove(resource.getPath());
244
245 return Optional.absent();
246 }
247
248 private class LoadAndRegisterFunction extends VoidFunction<Resource> {
249 @Override
250 public void doWith(Resource resource) {
251 log.debug("Loading {} ({})", resource, resource.getPath());
252 loadAndRegister(resource);
253 }
254 }
255
256 private class DefinitionDecoratorResolutionFunction extends VoidFunction<Resource> {
257 @Override
258 public void doWith(Resource resource) {
259 final Optional<FileDefinitionDecorator<T>> resolvedDecorator = resolveDecorator(resource);
260 if (resolvedDecorator.isPresent()) {
261 final FileDefinitionDecorator<T> decorator = resolvedDecorator.get();
262 log.info("File based definition decorator resolved from [{}] will be (re-)added to [{}] registry", resource.getPath(), RegistryTypeNameUtil.pluralTypeNameOf(registry));
263 resolvedDefinitionDecorators.put(resource.getPath(), decorator);
264 registry.addDecorator(decorator);
265 }
266 }
267 }
268 }