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