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.decoration;
35
36 import static info.magnolia.config.converters.RawDefinitionViewToMapConverter.rawViewToMap;
37 import static info.magnolia.config.maputil.ToMap.toMap;
38 import static info.magnolia.config.registry.DefinitionProvider.Problem.DefaultTypes.DECORATION;
39
40 import info.magnolia.cms.util.ExceptionUtil;
41 import info.magnolia.config.maputil.ConfigurationMapOverlay;
42 import info.magnolia.config.registry.DefinitionProvider;
43 import info.magnolia.config.registry.DefinitionProviderBuilder;
44 import info.magnolia.config.registry.DefinitionProviderProblemLogger;
45 import info.magnolia.config.registry.DefinitionRawView;
46 import info.magnolia.config.registry.DefinitionReferenceIdResolver;
47 import info.magnolia.config.registry.Registry;
48 import info.magnolia.config.registry.decoration.DefinitionDecorator;
49 import info.magnolia.config.source.DefinitionProviderWrapperWithProxyFallback;
50 import info.magnolia.config.source.raw.DefinitionRawViewMapWrapper;
51 import info.magnolia.config.source.yaml.YamlReader;
52 import info.magnolia.config.source.yaml.construct.IncludeFileYamlWithModificationPossibility;
53 import info.magnolia.config.source.yaml.construct.OverrideSource;
54 import info.magnolia.init.MagnoliaConfigurationProperties;
55 import info.magnolia.map2bean.Map2BeanTransformer;
56 import info.magnolia.objectfactory.Components;
57 import info.magnolia.resourceloader.Resource;
58 import info.magnolia.transformer.TransformationResult;
59
60 import java.util.HashMap;
61 import java.util.Map;
62 import java.util.Optional;
63
64 import org.apache.commons.collections4.MapUtils;
65 import org.apache.commons.lang3.exception.ExceptionUtils;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import com.google.common.collect.ImmutableList;
70
71 import lombok.SneakyThrows;
72
73
74
75
76
77
78
79 public class YamlDefinitionDecorator<T> extends AbstractFileDefinitionDecorator<T> {
80
81 private static final Logger log = LoggerFactory.getLogger(YamlDefinitionDecorator.class);
82
83 private final YamlReader yamlReader;
84
85 private final Resource yamlFile;
86
87 private final MagnoliaConfigurationProperties magnoliaConfigurationProperties;
88
89 private final YamlDefinitionDecoratorMetadata metadata;
90
91 private final DefinitionReferenceIdResolver referenceIdResolver;
92
93 private final Map2BeanTransformer map2BeanTransformer;
94
95
96
97
98 @Deprecated
99 public YamlDefinitionDecorator(
100 YamlDefinitionDecoratorMetadata metadata,
101 DefinitionReferenceIdResolver referenceIdResolver,
102 Resource decoratorYamlFile,
103 Map2BeanTransformer map2BeanTransformer) {
104 this(metadata, referenceIdResolver, decoratorYamlFile, map2BeanTransformer, Components.getComponent(MagnoliaConfigurationProperties.class));
105 }
106
107 public YamlDefinitionDecorator(
108 YamlDefinitionDecoratorMetadata metadata,
109 DefinitionReferenceIdResolver referenceIdResolver,
110 Resource decoratorYamlFile,
111 Map2BeanTransformer map2BeanTransformer,
112 MagnoliaConfigurationProperties magnoliaConfigurationProperties) {
113 this.yamlFile = decoratorYamlFile;
114 this.magnoliaConfigurationProperties = magnoliaConfigurationProperties;
115 this.yamlReader = new YamlReader();
116 this.metadata = metadata;
117 this.referenceIdResolver = referenceIdResolver;
118 this.map2BeanTransformer = map2BeanTransformer;
119 }
120
121 @Override
122 public Resource getDecoratorFile() {
123 return yamlFile;
124 }
125
126 @Override
127 public YamlDefinitionDecoratorMetadata metadata() {
128 return metadata;
129 }
130
131 @Override
132 public boolean appliesTo(DefinitionProvider<T> definitionProvider) {
133 return definitionProvider.getMetadata().getReferenceId().equals(referenceIdResolver.getReferenceId(metadata.getDecoratedDefinitionReference()));
134 }
135
136 @Override
137 public DefinitionProvider<T> decorate(final DefinitionProvider<T> definitionProvider) {
138 final DefinitionProviderBuilder<T> definitionProviderBuilder = referenceIdResolver instanceof Registry ? ((Registry) referenceIdResolver).newDefinitionProviderBuilder() : DefinitionProviderBuilder.newBuilder();
139
140 this.yamlReader.registerCustomConstruct(OverrideSource.TAG, new OverrideSource(definitionProviderBuilder::addProblem));
141 this.yamlReader.registerCustomMultiConstruct(IncludeFileYamlWithModificationPossibility.TAG_PREFIX, new IncludeFileYamlWithModificationPossibility(yamlReader, this.yamlFile.getOrigin(), definitionProviderBuilder::addProblem));
142
143 DefinitionRawView raw = definitionProvider.getRaw();
144 Map<String, Object> decoratedMapRepresentation = new HashMap<>();
145 Map<String, Object> definitionData = new HashMap<>();
146 try {
147
148
149 definitionData = raw instanceof DefinitionRawViewMapWrapper ? ((DefinitionRawViewMapWrapper) raw).getRawMap() : rawViewToMap(raw);
150 } catch (Exception e) {
151 definitionProviderBuilder.addProblem(
152 DefinitionProvider.Problem
153 .severe()
154 .withType(DECORATION)
155 .withTitle(String.format("Failed to parse raw definition from [%s] during decorating with [%s]", definitionProvider.getMetadata().getName(), yamlFile.getPath()))
156 .withRelatedException(e)
157 .withDetails(ExceptionUtil.exceptionToWords(
158 Optional
159 .ofNullable(ExceptionUtils.getRootCause(e))
160 .orElse(e)))
161 .build());
162 }
163 try {
164 if (MapUtils.isNotEmpty(definitionData)) {
165 final Map<String, Object> decoratorData = parseYamlFile();
166
167 if (decoratorData.isEmpty()) {
168 definitionProviderBuilder
169 .addProblem(
170 DefinitionProvider.Problem
171 .major()
172 .withType(DECORATION)
173 .withTitle(String.format("Decoration data from [%s] is empty", yamlFile.getPath()))
174 .build());
175 }
176
177 decoratedMapRepresentation = ConfigurationMapOverlay
178 .of(definitionData)
179 .by(decoratorData)
180 .at(metadata.getDecoratedPath())
181 .overlay();
182 }
183 } catch (Exception e) {
184 decoratedMapRepresentation = new HashMap<>();
185 definitionProviderBuilder.addProblem(
186 DefinitionProvider.Problem
187 .major()
188 .withType(DECORATION)
189 .withTitle(String.format("Failed to parse decoration data from [%s]", yamlFile.getPath()))
190 .withRelatedException(e)
191 .withDetails(ExceptionUtil.exceptionToWords(
192 Optional
193 .ofNullable(ExceptionUtils.getRootCause(e))
194 .orElse(e)))
195 .build());
196 }
197
198
199 final TransformationResult<T> transformationResult =
200 map2BeanTransformer
201 .transform(decoratedMapRepresentation, definitionProvider.getMetadata().getType().baseClass());
202
203 final DefinitionProvider<T> decoratedDefinitionProvider = definitionProviderBuilder
204 .metadata(definitionProvider.getMetadata())
205 .rawView(new DefinitionRawViewMapWrapper(decoratedMapRepresentation))
206 .decorators(ImmutableList
207 .<DefinitionDecorator<T>>builder()
208 .addAll(definitionProvider.getDecorators())
209 .add(YamlDefinitionDecorator.this)
210 .build())
211 .buildFromTransformationResult(transformationResult);
212
213 log.info("Applied {} to definition provider [{}]", this, definitionProvider.getMetadata());
214
215 DefinitionProviderProblemLogger.withLoggingContext(log, magnoliaConfigurationProperties.getBooleanProperty("magnolia.develop")).logProblems(decoratedDefinitionProvider);
216
217 return new DefinitionProviderWrapperWithProxyFallback<>(decoratedDefinitionProvider, definitionProvider.get());
218 }
219
220 @SneakyThrows
221
222 protected final Map<String, Object> parseYamlFile() {
223 return toMap(yamlReader.read(yamlFile));
224 }
225
226 @Override
227 public String toString() {
228 return String.format("YAML file based decorator from [%s]", yamlFile.getPath());
229 }
230 }