Skip to content

Commit d6750d3

Browse files
ContentPackageOsgiConfigPostProcessor: Support Combined JSON files (#86)
1 parent 5503772 commit d6750d3

10 files changed

Lines changed: 150 additions & 39 deletions

File tree

changes.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
<action type="update" dev="sseifert" issue="85">
2828
ContentPackageOsgiConfigPostProcessor: Write OSGi configurations as .cfg.json files instead of .config files.
2929
</action>
30+
<action type="update" dev="sseifert" issue="86"><![CDATA[
31+
ContentPackageOsgiConfigPostProcessor: Accept both <a href="https://devops.wcm.io/conga/plugins/sling//osgi-config-combined-json.html">Combined JSON files</a>
32+
and Sling Provisioning File Format as input for generating OSGi configurations.
33+
]]></action>
3034
</release>
3135

3236
<release version="2.19.10" date="2023-12-18">

conga-aem-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/postprocessor/ContentPackageOsgiConfigPostProcessor.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.io.InputStream;
3030
import java.io.OutputStream;
3131
import java.nio.charset.StandardCharsets;
32+
import java.nio.file.Files;
3233
import java.util.Dictionary;
3334
import java.util.HashMap;
3435
import java.util.List;
@@ -39,25 +40,24 @@
3940
import org.apache.sling.provisioning.model.Model;
4041
import org.slf4j.Logger;
4142

42-
import com.google.common.collect.ImmutableList;
43-
import com.google.common.collect.ImmutableMap;
44-
4543
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4644
import io.wcm.devops.conga.generator.GeneratorException;
4745
import io.wcm.devops.conga.generator.plugins.postprocessor.AbstractPostProcessor;
4846
import io.wcm.devops.conga.generator.spi.context.FileContext;
4947
import io.wcm.devops.conga.generator.spi.context.FileHeaderContext;
5048
import io.wcm.devops.conga.generator.spi.context.PostProcessorContext;
5149
import io.wcm.devops.conga.plugins.aem.util.ContentPackageUtil;
50+
import io.wcm.devops.conga.plugins.sling.postprocessor.JsonOsgiConfigPostProcessor;
5251
import io.wcm.devops.conga.plugins.sling.util.ConfigConsumer;
52+
import io.wcm.devops.conga.plugins.sling.util.JsonOsgiConfigUtil;
5353
import io.wcm.devops.conga.plugins.sling.util.OsgiConfigUtil;
5454
import io.wcm.devops.conga.plugins.sling.util.ProvisioningUtil;
5555
import io.wcm.tooling.commons.contentpackagebuilder.ContentPackage;
5656
import io.wcm.tooling.commons.contentpackagebuilder.ContentPackageBuilder;
5757

5858
/**
59-
* Transforms a Sling Provisioning file into OSGi configurations (ignoring all other provisioning contents)
60-
* and then packages them up in an AEM content package to be deployed via CRX package manager.
59+
* Transforms a Sling Provisioning file or Combined JSON file as used by CONGA Sling Plugin
60+
* into OSGi configurations and then packages them up in an AEM content package to be deployed via CRX package manager.
6161
*/
6262
public class ContentPackageOsgiConfigPostProcessor extends AbstractPostProcessor {
6363

@@ -73,7 +73,8 @@ public String getName() {
7373

7474
@Override
7575
public boolean accepts(FileContext file, PostProcessorContext context) {
76-
return ProvisioningUtil.isProvisioningFile(file);
76+
return ProvisioningUtil.isProvisioningFile(file)
77+
|| StringUtils.endsWith(file.getFile().getName(), JsonOsgiConfigPostProcessor.FILE_EXTENSION);
7778
}
7879

7980
@Override
@@ -88,14 +89,20 @@ public List<FileContext> apply(FileContext fileContext, PostProcessorContext con
8889
FileHeaderContext fileHeader = extractFileHeader(fileContext, context);
8990

9091
// generate OSGi configurations
91-
Model model = ProvisioningUtil.getModel(fileContext);
92+
Model model;
93+
if (ProvisioningUtil.isProvisioningFile(fileContext)) {
94+
model = ProvisioningUtil.getModel(fileContext);
95+
}
96+
else {
97+
model = JsonOsgiConfigUtil.readToProvisioningModel(file);
98+
}
9299

93100
// check if any osgi configuration is present
94101
boolean hasAnyConfig = !ProvisioningUtil.visitOsgiConfigurations(model,
95102
(ConfigConsumer<Boolean>)(path, properties) -> true).isEmpty();
96103

97104
// create AEM content package with configurations
98-
File zipFile = new File(file.getParentFile(), FilenameUtils.getBaseName(file.getName()) + ".zip");
105+
File zipFile = new File(file.getParentFile(), getBaseFileName(file.getName()) + ".zip");
99106
logger.info("Generate {}", zipFile.getCanonicalPath());
100107

101108
String rootPath = ContentPackageUtil.getMandatoryProp(options, PROPERTY_PACKAGE_ROOT_PATH);
@@ -113,19 +120,19 @@ public List<FileContext> apply(FileContext fileContext, PostProcessorContext con
113120
else {
114121
// create folder for root path if package is empty otherwise
115122
// (to make sure probably already existing config is overridden/cleaned)
116-
contentPackage.addContent(rootPath, ImmutableMap.of("jcr:primaryType", "nt:folder"));
123+
contentPackage.addContent(rootPath, Map.of("jcr:primaryType", "nt:folder"));
117124
}
118125
}
119126

120127
// delete provisioning file after transformation
121-
file.delete();
128+
Files.delete(file.toPath());
122129

123130
// set force to true by default for CONGA-generated packages (but allow override from role definition)
124131
Map<String, Object> modelOptions = new HashMap<>();
125132
modelOptions.put("force", true);
126133
modelOptions.putAll(fileContext.getModelOptions());
127134

128-
return ImmutableList.of(new FileContext().file(zipFile).modelOptions(modelOptions));
135+
return List.of(new FileContext().file(zipFile).modelOptions(modelOptions));
129136
}
130137
catch (IOException ex) {
131138
throw new GeneratorException("Unable to post-process sling provisioning OSGi configurations.", ex);
@@ -168,12 +175,21 @@ public Void accept(String path, Dictionary<String, Object> properties) throws IO
168175
}
169176
finally {
170177
// remove temporary file
171-
tempFile.delete();
178+
Files.delete(tempFile.toPath());
172179
}
173180
return null;
174181
}
175182
});
176183
return !result.isEmpty();
177184
}
178185

186+
private String getBaseFileName(String fileName) {
187+
if (StringUtils.endsWith(fileName, JsonOsgiConfigPostProcessor.FILE_EXTENSION)) {
188+
return StringUtils.substringBeforeLast(fileName, JsonOsgiConfigPostProcessor.FILE_EXTENSION);
189+
}
190+
else {
191+
return FilenameUtils.getBaseName(fileName);
192+
}
193+
}
194+
179195
}

conga-aem-plugin/src/test/java/io/wcm/devops/conga/plugins/aem/postprocessor/ContentPackageOsgiConfigPostProcessorTest.java

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@
4848
import org.w3c.dom.Document;
4949
import org.zeroturnaround.zip.ZipUtil;
5050

51-
import com.google.common.collect.ImmutableMap;
52-
5351
import io.wcm.devops.conga.generator.spi.PostProcessorPlugin;
5452
import io.wcm.devops.conga.generator.spi.context.FileContext;
5553
import io.wcm.devops.conga.generator.spi.context.PluginContextOptions;
@@ -61,7 +59,7 @@ class ContentPackageOsgiConfigPostProcessorTest {
6159

6260
private PostProcessorPlugin underTest;
6361

64-
private static final Map<String, Object> PACKAGE_OPTIONS = ImmutableMap.<String, Object>of(
62+
private static final Map<String, Object> PACKAGE_OPTIONS = Map.of(
6563
PROPERTY_PACKAGE_GROUP, "myGroup",
6664
PROPERTY_PACKAGE_NAME, "myName",
6765
PROPERTY_PACKAGE_DESCRIPTION, "myDesc",
@@ -74,15 +72,33 @@ void setUp() {
7472
}
7573

7674
@Test
77-
void testPostProcess() throws Exception {
75+
void testPostProcess_Provisioning() throws Exception {
7876
// prepare provisioning file
79-
File target = new File("target/" + ContentPackageOsgiConfigPostProcessor.NAME + "-test");
77+
File target = new File("target/" + ContentPackageOsgiConfigPostProcessor.NAME + "-provisioning");
8078
if (target.exists()) {
8179
FileUtils.deleteDirectory(target);
8280
}
8381
File contentPackageFile = new File(target, "test.txt");
8482
FileUtils.copyFile(new File(getClass().getResource("/provisioning/provisioning.txt").toURI()), contentPackageFile);
8583

84+
postProcess_assertResult(target, contentPackageFile, "myDesc\n---\nSample comment in provisioning.txt");
85+
}
86+
87+
@Test
88+
void testPostProcess_JSON() throws Exception {
89+
// prepare provisioning file
90+
File target = new File("target/" + ContentPackageOsgiConfigPostProcessor.NAME + "-json");
91+
if (target.exists()) {
92+
FileUtils.deleteDirectory(target);
93+
}
94+
File contentPackageFile = new File(target, "test.osgiconfig.json");
95+
FileUtils.copyFile(new File(getClass().getResource("/osgi-config-json/sample.osgiconfig.json").toURI()), contentPackageFile);
96+
97+
postProcess_assertResult(target, contentPackageFile, "myDesc");
98+
}
99+
100+
private void postProcess_assertResult(File target, File contentPackageFile,
101+
String expectedPackageDescriptionProperty) throws Exception {
86102
// post-process
87103
FileContext fileContext = new FileContext()
88104
.file(contentPackageFile)
@@ -124,7 +140,7 @@ void testPostProcess() throws Exception {
124140
Document propsXml = getXmlFromZip(zipFile, "META-INF/vault/properties.xml");
125141
assertXpathEvaluatesTo("myGroup", "/properties/entry[@key='group']", propsXml);
126142
assertXpathEvaluatesTo("myName", "/properties/entry[@key='name']", propsXml);
127-
assertXpathEvaluatesTo("myDesc\n---\nSample comment in provisioning.txt", "/properties/entry[@key='description']", propsXml);
143+
assertXpathEvaluatesTo(expectedPackageDescriptionProperty, "/properties/entry[@key='description']", propsXml);
128144
assertXpathEvaluatesTo("1.5", "/properties/entry[@key='version']", propsXml);
129145
assertXpathEvaluatesTo("container", "/properties/entry[@key='packageType']", propsXml);
130146

@@ -134,13 +150,30 @@ void testPostProcess() throws Exception {
134150
@Test
135151
void testPostProcess_EmptyProvisioning() throws Exception {
136152
// prepare provisioning file
137-
File target = new File("target/" + ContentPackageOsgiConfigPostProcessor.NAME + "-test-empty");
153+
File target = new File("target/" + ContentPackageOsgiConfigPostProcessor.NAME + "-empty-provisioning");
138154
if (target.exists()) {
139155
FileUtils.deleteDirectory(target);
140156
}
141157
File contentPackageFile = new File(target, "test.txt");
142158
FileUtils.copyFile(new File(getClass().getResource("/provisioning/provisioning_empty.txt").toURI()), contentPackageFile);
143159

160+
postProcess_Empty_assertResult(target, contentPackageFile);
161+
}
162+
163+
@Test
164+
void testPostProcess_EmptyJSON() throws Exception {
165+
// prepare provisioning file
166+
File target = new File("target/" + ContentPackageOsgiConfigPostProcessor.NAME + "-empty-json");
167+
if (target.exists()) {
168+
FileUtils.deleteDirectory(target);
169+
}
170+
File contentPackageFile = new File(target, "test.osgiconfig.json");
171+
FileUtils.copyFile(new File(getClass().getResource("/osgi-config-json/sample_empty.osgiconfig.json").toURI()), contentPackageFile);
172+
173+
postProcess_Empty_assertResult(target, contentPackageFile);
174+
}
175+
176+
private void postProcess_Empty_assertResult(File target, File contentPackageFile) throws Exception {
144177
// post-process
145178
FileContext fileContext = new FileContext()
146179
.file(contentPackageFile)

conga-aem-plugin/src/test/java/io/wcm/devops/conga/plugins/aem/postprocessor/ContentPackagePostProcessorTest.java

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.io.File;
3939
import java.nio.charset.StandardCharsets;
4040
import java.util.HashMap;
41+
import java.util.List;
4142
import java.util.Map;
4243

4344
import org.apache.commons.io.FileUtils;
@@ -46,9 +47,6 @@
4647
import org.slf4j.LoggerFactory;
4748
import org.w3c.dom.Document;
4849

49-
import com.google.common.collect.ImmutableList;
50-
import com.google.common.collect.ImmutableMap;
51-
5250
import io.wcm.devops.conga.generator.UrlFileManager;
5351
import io.wcm.devops.conga.generator.spi.PostProcessorPlugin;
5452
import io.wcm.devops.conga.generator.spi.context.FileContext;
@@ -70,26 +68,25 @@ void setUp() {
7068

7169
@Test
7270
void testPostProcess() throws Exception {
73-
Map<String, Object> options = new HashMap<String, Object>();
71+
Map<String, Object> options = new HashMap<>();
7472
options.put(PROPERTY_PACKAGE_GROUP, "myGroup");
7573
options.put(PROPERTY_PACKAGE_NAME, "myName");
7674
options.put(PROPERTY_PACKAGE_ROOT_PATH, "/content/test");
7775
options.put(PROPERTY_PACKAGE_AC_HANDLING, "ignore");
7876
options.put(PROPERTY_PACKAGE_PACKAGE_TYPE, "content");
7977
options.put(PROPERTY_PACKAGE_THUMBNAIL_IMAGE, "classpath:/package/thumbnail.png");
80-
options.put(PROPERTY_PACKAGE_FILTERS, ImmutableList.of(
81-
ImmutableMap.<String, Object>of("filter", "/content/test/1"),
82-
ImmutableMap.<String, Object>of("filter", "/content/test/2",
83-
"rules", ImmutableList.of(ImmutableMap.<String, Object>of("rule", "include", "pattern", "pattern1"),
84-
ImmutableMap.<String, Object>of("rule", "exclude", "pattern", "pattern2")))
85-
));
86-
options.put(PROPERTY_PACKAGE_PROPERTIES, ImmutableMap.<String,Object>of(
78+
options.put(PROPERTY_PACKAGE_FILTERS, List.of(
79+
Map.of("filter", "/content/test/1"),
80+
Map.of("filter", "/content/test/2",
81+
"rules", List.of(Map.of("rule", "include", "pattern", "pattern1"),
82+
Map.of("rule", "exclude", "pattern", "pattern2")))));
83+
options.put(PROPERTY_PACKAGE_PROPERTIES, Map.of(
8784
"prop1", "value1",
8885
"my.custom.prop2", 123));
89-
options.put(PROPERTY_PACKAGE_FILES, ImmutableList.of(
90-
ImmutableMap.<String, Object>of("url", "classpath:/package/thumbnail.png", "path", "/content/image.png"),
91-
ImmutableMap.<String, Object>of("file", "README.txt", "dir", "readme", "path", "/content/README.txt", "delete", true),
92-
ImmutableMap.<String, Object>of("fileMatch", "file_(.*).txt", "dir", "files", "path", "/content/files/$1.txt", "delete", true)));
86+
options.put(PROPERTY_PACKAGE_FILES, List.of(
87+
Map.of("url", "classpath:/package/thumbnail.png", "path", "/content/image.png"),
88+
Map.of("file", "README.txt", "dir", "readme", "path", "/content/README.txt", "delete", true),
89+
Map.of("fileMatch", "file_(.*).txt", "dir", "files", "path", "/content/files/$1.txt", "delete", true)));
9390

9491
// prepare JSON file
9592
File target = new File("target/" + ContentPackagePostProcessor.NAME + "-test");

conga-aem-plugin/src/test/java/io/wcm/devops/conga/plugins/aem/postprocessor/ContentPackagePropertiesPostProcessorTest.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
import org.junit.jupiter.api.Test;
3131
import org.slf4j.LoggerFactory;
3232

33-
import com.google.common.collect.ImmutableMap;
34-
3533
import io.wcm.devops.conga.generator.spi.PostProcessorPlugin;
3634
import io.wcm.devops.conga.generator.spi.context.FileContext;
3735
import io.wcm.devops.conga.generator.spi.context.PluginContextOptions;
@@ -56,7 +54,7 @@ void testContentPackage() throws Exception {
5654
.file(new File("src/test/resources/package/example.zip"));
5755

5856
// post-process
59-
applyPlugin(fileContext, ImmutableMap.of());
57+
applyPlugin(fileContext, Map.of());
6058

6159
// validate
6260
Map<String, Object> props = (Map<String, Object>)fileContext.getModelOptions().get(ContentPackagePropertiesPostProcessor.MODEL_OPTIONS_PROPERTY);
@@ -74,7 +72,7 @@ void testContentPackageOverridePackageType() throws Exception {
7472
.file(new File("src/test/resources/package/example.zip"));
7573

7674
// post-process
77-
applyPlugin(fileContext, ImmutableMap.of("contentPackage", ImmutableMap.of("packageType", "mytype")));
75+
applyPlugin(fileContext, Map.of("contentPackage", Map.of("packageType", "mytype")));
7876

7977
// validate
8078
Map<String, Object> props = (Map<String, Object>)fileContext.getModelOptions().get(ContentPackagePropertiesPostProcessor.MODEL_OPTIONS_PROPERTY);
@@ -88,7 +86,7 @@ void testNonContentPackage() throws Exception {
8886
.file(new File("src/test/resources/package/no-content-package.zip"));
8987

9088
// post-process
91-
applyPlugin(fileContext, ImmutableMap.of());
89+
applyPlugin(fileContext, Map.of());
9290

9391
// validate
9492
assertNull(fileContext.getModelOptions().get(ContentPackagePropertiesPostProcessor.MODEL_OPTIONS_PROPERTY));
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"configurations": {
3+
"my.pid": {
4+
"stringProperty": "value1",
5+
"stringArrayProperty": ["v1","v2","v3"],
6+
"booleanProperty": true,
7+
"longProperty": 999999999999
8+
},
9+
"my.factory-my.pid": {
10+
"stringProperty": "value2"
11+
}
12+
},
13+
"configurations:mode1": {
14+
"my.factory-my.pid2": {
15+
"stringProperty": "value3"
16+
}
17+
},
18+
"configurations:mode2": {
19+
"my.pid2": {
20+
"stringProperty": "value4"
21+
}
22+
}
23+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

tooling/conga-aem-maven-plugin/src/it/example/src/main/roles/aem.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@ files:
2323
filters:
2424
- filter: /apps/sample
2525

26+
# Define a AEM content package containing OSGi configurations from a JSON file
27+
- file: config-sample.osgiconfig.json
28+
dir: packages
29+
template: config-sample.osgiconfig.json.hbs
30+
# Transform OSGi configs from provisoning file to AEM content package
31+
postProcessors:
32+
- aem-contentpackage-osgiconfig
33+
postProcessorOptions:
34+
contentPackage:
35+
name: config-sample-from-json
36+
packageType: container
37+
description: The description of the sample package.
38+
version: "${version}"
39+
rootPath: /apps/sample/config
40+
filters:
41+
- filter: /apps/sample
42+
2643
# Define a AEM content package with some JCR content (Sling Mapping Example)
2744
- file: sling-mapping.json
2845
dir: packages
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"configurations": {
3+
"my.pid": {
4+
"heapspaceMax": "{{jvm.heapspace.max}}",
5+
"booleanProp": true,
6+
"numberProp": 123,
7+
"arrayProp": ["v1","v2","v3"],
8+
"numberArrayProp": [1,2]
9+
}
10+
},
11+
"configurations:mode1": {
12+
"my.pid2": {
13+
"stringProperty": "{{var1}}",
14+
"stringProperty2": "{{var2}}"
15+
}
16+
}
17+
}

tooling/conga-aem-maven-plugin/src/it/example/src/main/templates/aem/sling-provisioning.provisioning.hbs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
my.pid
66
heapspaceMax="{{jvm.heapspace.max}}"
7+
booleanProp=B"true"
8+
numberProp=I"123"
9+
arrayProp=["v1","v2","v3"]
10+
numberArrayProp=I["1","2"]
711

812
[configurations runModes=mode1]
913

1014
my.pid2
1115
stringProperty="{{var1}}"
16+
stringProperty2="{{var2}}"

0 commit comments

Comments
 (0)