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.cms.util.ExceptionUtil.exceptionToWords;
37 import static info.magnolia.config.maputil.ToMap.toMap;
38
39 import info.magnolia.config.NamedDefinition;
40 import info.magnolia.config.map2bean.Map2BeanTransformer;
41 import info.magnolia.config.registry.AbstractDefinitionProviderWrapper;
42 import info.magnolia.config.registry.DefinitionMetadataBuilder;
43 import info.magnolia.config.registry.DefinitionProvider;
44 import info.magnolia.config.registry.DefinitionProviderBuilder;
45 import info.magnolia.config.registry.DefinitionRawView;
46 import info.magnolia.config.source.raw.DefinitionRawViewMapWrapper;
47 import info.magnolia.resourceloader.Resource;
48 import info.magnolia.resourceloader.ResourceOrigin;
49 import info.magnolia.resourceloader.ResourceOrigin.ResourceNotFoundException;
50
51 import java.util.Map;
52 import java.util.Set;
53
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import com.google.common.collect.Sets;
58
59
60
61
62
63
64
65 public class YamlDefinitionProvider<T> extends AbstractDefinitionProviderWrapper<T> {
66
67 private static final Logger log = LoggerFactory.getLogger(YamlDefinitionProvider.class);
68 private static final String NAME_PROPERTY = "name";
69 private static final int MODIFICATION_CHECK_THRESHOLD = 1000;
70
71 private final Set<String> dependencies = Sets.newHashSet();
72 private final YamlReader yamlReader;
73 private final YamlConfigurationSource<T> relatedSource;
74 private final String resourcePath;
75 private final ResourceOrigin<?> resourceOrigin;
76 private final Map2BeanTransformer map2BeanTransformer;
77
78 private long lastModificationCheck = -1;
79 private long lastResolved = -1;
80
81 private DefinitionProvider<T> delegate;
82
83 public YamlDefinitionProvider(YamlConfigurationSource<T> relatedSource, Resource yamlResource, Map2BeanTransformer map2BeanTransformer, YamlReader yamlReader) {
84 this.relatedSource = relatedSource;
85 this.map2BeanTransformer = map2BeanTransformer;
86 this.yamlReader = yamlReader;
87 this.resourcePath = yamlResource.getPath();
88 this.resourceOrigin = yamlResource.getOrigin();
89 }
90
91 @Override
92 public long getLastModified() {
93 long lastModified = resourceOrigin.getByPath(resourcePath).getLastModified();
94 for (final String dependencyPath : dependencies) {
95 try {
96 final Resource dependency = resourceOrigin.getByPath(dependencyPath);
97 if (dependency.getLastModified() > lastModified) {
98 log.info("Definition at [{}] will be re-resolved since its dependency at [{}] has changed", dependencyPath, resourcePath);
99 }
100 lastModified = Math.max(dependency.getLastModified(), lastModified);
101 } catch (ResourceNotFoundException e) {
102 log.info("Resource [{}] which is a dependency of [{}], is no longer present. Returning current timestamp as modified date in order to enforce definition re-resolution", dependencyPath, resourcePath);
103 return System.currentTimeMillis();
104 }
105 }
106
107 return lastModified;
108 }
109
110 @Override
111 protected DefinitionProvider<T> getDelegate() {
112 long time = System.currentTimeMillis();
113
114 if (time - lastModificationCheck < MODIFICATION_CHECK_THRESHOLD) {
115 return delegate;
116 }
117
118 lastModificationCheck = time;
119 if (resourceOrigin.hasPath(resourcePath)) {
120 if (getLastModified() > lastResolved) {
121 delegate = resolve();
122 }
123 } else {
124 log.debug("Resource origin no longer contains the definition file [{}], which has most likely been removed, will not attempt to re-resolve definition");
125 }
126 return delegate;
127 }
128
129 private DefinitionProvider<T> resolve() {
130
131 this.dependencies.clear();
132
133 final Resource yamlResource = resourceOrigin.getByPath(resourcePath);
134 final DefinitionProviderBuilder<T> builder = DefinitionProviderBuilder.newBuilder();
135 final DefinitionMetadataBuilder metadataBuilder = relatedSource.createMetadata(yamlResource);
136 builder.metadata(metadataBuilder);
137
138 final YamlReader.YamlConversionResult yamlConversionResult;
139 try {
140
141 yamlConversionResult = yamlReader.readWithDependencies(yamlResource);
142
143 this.dependencies.addAll(yamlConversionResult.getDependencies());
144
145 final Map<String, Object> map = toMap(yamlConversionResult.getResult());
146 final DefinitionRawView raw = new DefinitionRawViewMapWrapper(map);
147 builder.rawView(raw);
148
149
150
151
152 if (NamedDefinition.class.isAssignableFrom(relatedSource.getRootType())) {
153 final Object nameValue = map.get(NAME_PROPERTY);
154 if (nameValue instanceof String) {
155 final String name = (String) map.get(NAME_PROPERTY);
156 metadataBuilder.name(name);
157 } else if (nameValue == null) {
158 map.put(NAME_PROPERTY, metadataBuilder.getName());
159 }
160 }
161
162
163 final T bean = map2BeanTransformer.toBean(map, relatedSource.getRootType());
164 builder.definition(bean);
165 } catch (Exception e) {
166
167 final String errorMessage = exceptionToWords(e);
168 builder.addErrorMessage(errorMessage);
169 log.warn("Problem while registering {} from {}: {}", relatedSource.getRegistry().type(), yamlResource, errorMessage, e);
170 }
171
172 this.lastResolved = System.currentTimeMillis();
173
174 return builder.build();
175 }
176 }