1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.maven.plugins.slinginitialcontenttransform;
21
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.nio.file.Files;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.jar.Attributes;
30 import java.util.jar.Manifest;
31 import java.util.stream.Collectors;
32 import java.util.zip.ZipEntry;
33 import java.util.zip.ZipOutputStream;
34
35 import org.apache.commons.io.FilenameUtils;
36 import org.apache.commons.io.IOUtils;
37 import org.apache.commons.lang3.StringUtils;
38 import org.apache.jackrabbit.vault.packaging.PackageType;
39 import org.apache.maven.plugin.AbstractMojo;
40 import org.apache.maven.plugin.MojoExecutionException;
41 import org.apache.maven.plugin.MojoFailureException;
42 import org.apache.maven.plugins.annotations.Component;
43 import org.apache.maven.plugins.annotations.LifecyclePhase;
44 import org.apache.maven.plugins.annotations.Mojo;
45 import org.apache.maven.plugins.annotations.Parameter;
46 import org.apache.maven.project.MavenProject;
47 import org.apache.maven.project.MavenProjectHelper;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import io.wcm.tooling.commons.contentpackagebuilder.ContentPackage;
52 import io.wcm.tooling.commons.contentpackagebuilder.ContentPackageBuilder;
53 import io.wcm.tooling.commons.contentpackagebuilder.PackageFilter;
54
55
56
57
58
59
60
61
62 @Mojo(name = "transform", requiresProject = true, threadSafe = true, defaultPhase = LifecyclePhase.PACKAGE)
63 public class TransformMojo extends AbstractMojo {
64
65 private static final String CLASSIFIER_CONTENT = "content";
66 private static final String CLASSIFIER_BUNDLE = "bundle";
67 private static final String MANIFEST_FILE = "META-INF/MANIFEST.MF";
68
69 private static final Logger log = LoggerFactory.getLogger(TransformMojo.class);
70
71
72
73
74 @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.jar", required = true)
75 private File file;
76
77
78
79
80 @Parameter(defaultValue = "${project.groupId}", required = true)
81 private String group;
82
83
84
85
86 @Parameter(defaultValue = "true", required = true)
87 private boolean generateContent;
88
89
90
91
92 @Parameter(defaultValue = "true", required = true)
93 private boolean generateBundle;
94
95
96
97
98 @Parameter
99 private Map<String, String> xmlNamespaces;
100
101
102
103
104 @Parameter(property = "slinginitialcontenttransform.skip", defaultValue = "false")
105 private boolean skip;
106
107 @Parameter(property = "project", required = true, readonly = true)
108 private MavenProject project;
109 @Component
110 private MavenProjectHelper projectHelper;
111
112 @Override
113 public void execute() throws MojoExecutionException, MojoFailureException {
114
115 log.warn("The plugin sling-initial-content-transform plugin is deprecated, it is not longer required. Please remove it from your project.");
116
117 if (skip) {
118 log.debug("Skipping execution.");
119 return;
120 }
121 if (!StringUtils.equals(project.getPackaging(), "jar")) {
122 log.debug("Skipping execution - not a jar project: {}", project.getPackaging());
123 return;
124 }
125 if (!file.exists()) {
126 log.warn("File does not exist: {}", file.getPath());
127 return;
128 }
129
130 try (OsgiBundleFile osgiBundle = new OsgiBundleFile(file)) {
131 if (!osgiBundle.hasContent()) {
132 log.debug("Skipping execution - bundle does not contain Sling-Initial-Content.");
133 return;
134 }
135 transformBundle(osgiBundle);
136 }
137 catch (IOException ex) {
138 throw new MojoExecutionException("Unable to transform bundle.", ex);
139 }
140 }
141
142
143
144
145
146 private void transformBundle(OsgiBundleFile osgiBundle) throws IOException {
147 if (generateContent) {
148 File contentPackageFile = createContentPackage(osgiBundle);
149 projectHelper.attachArtifact(project, "zip", CLASSIFIER_CONTENT, contentPackageFile);
150 }
151 if (generateBundle) {
152 File bundleFile = createBundleWithoutContent(osgiBundle);
153 projectHelper.attachArtifact(project, "jar", CLASSIFIER_BUNDLE, bundleFile);
154 }
155 }
156
157
158
159
160
161
162
163 private File createContentPackage(OsgiBundleFile osgiBundle) throws IOException {
164 String contentPackageName = project.getBuild().getFinalName() + "-" + CLASSIFIER_CONTENT + ".zip";
165 File contentPackageFile = new File(project.getBuild().getDirectory(), contentPackageName);
166 if (contentPackageFile.exists()) {
167 Files.delete(contentPackageFile.toPath());
168 }
169
170 ContentPackageBuilder contentPackageBuilder = new ContentPackageBuilder()
171 .group(this.group)
172 .name(project.getArtifactId() + "-" + CLASSIFIER_CONTENT)
173 .version(project.getVersion())
174 .packageType(PackageType.APPLICATION.name().toLowerCase());
175 for (ContentMapping mapping : osgiBundle.getContentMappings()) {
176 contentPackageBuilder.filter(new PackageFilter(mapping.getContentPath()));
177 }
178 for (Map.Entry<String, String> namespace : osgiBundle.getNamespaces().entrySet()) {
179 contentPackageBuilder.xmlNamespace(namespace.getKey(), namespace.getValue());
180 }
181 if (xmlNamespaces != null) {
182 for (Map.Entry<String, String> namespace : xmlNamespaces.entrySet()) {
183 contentPackageBuilder.xmlNamespace(namespace.getKey(), namespace.getValue());
184 }
185 }
186 try (ContentPackage contentPackage = contentPackageBuilder.build(contentPackageFile)) {
187 for (ContentMapping mapping : osgiBundle.getContentMappings()) {
188 List<BundleEntry> entries = osgiBundle.getContentEntries(mapping).collect(Collectors.toList());
189
190
191 CollectNonDirectoryPathsProcessor nonDirectoryPaths = new CollectNonDirectoryPathsProcessor();
192 for (BundleEntry entry : entries) {
193 processContent(contentPackage, entry, mapping, nonDirectoryPaths);
194 }
195
196
197 WriteContentProcessor writeContent = new WriteContentProcessor(nonDirectoryPaths.getPaths());
198 for (BundleEntry entry : entries) {
199 processContent(contentPackage, entry, mapping, writeContent);
200 }
201 }
202 }
203
204 log.info("Created package with Sling-Initial-Content: {}", contentPackageFile.getName());
205 return contentPackageFile;
206 }
207
208
209
210
211
212
213
214
215
216 private void processContent(ContentPackage contentPackage, BundleEntry entry, ContentMapping mapping,
217 BundleEntryProcessor processor) throws IOException {
218 String extension = FilenameUtils.getExtension(entry.getPath());
219 if (entry.isDirectory()) {
220 String path = StringUtils.removeEnd(entry.getPath(), "/");
221 processor.directory(path, contentPackage);
222 }
223 else if (mapping.isJson() && StringUtils.equals(extension, "json")) {
224 String path = StringUtils.substringBeforeLast(entry.getPath(), ".json");
225 processor.jsonContent(path, entry, contentPackage);
226 }
227 else if (mapping.isXml() && StringUtils.equals(extension, "xml")) {
228 String path = StringUtils.substringBeforeLast(entry.getPath(), ".xml");
229 processor.xmlContent(path, entry, contentPackage);
230 }
231 else {
232 String path = entry.getPath();
233 processor.binaryContent(path, entry, contentPackage);
234 }
235 }
236
237
238
239
240
241
242
243 private File createBundleWithoutContent(OsgiBundleFile osgiBundle) throws IOException {
244 String bundleFileName = project.getBuild().getFinalName() + "-" + CLASSIFIER_BUNDLE + ".jar";
245 File bundleFile = new File(project.getBuild().getDirectory(), bundleFileName);
246 if (bundleFile.exists()) {
247 Files.delete(bundleFile.toPath());
248 }
249
250 try (FileOutputStream fos = new FileOutputStream(bundleFile);
251 ZipOutputStream zos = new ZipOutputStream(fos)) {
252 List<BundleEntry> entries = osgiBundle.getNonContentEntries().collect(Collectors.toList());
253 for (BundleEntry entry : entries) {
254 zos.putNextEntry(new ZipEntry(entry.getPath()));
255 if (!entry.isDirectory()) {
256 try (InputStream is = entry.getInputStream()) {
257 if (StringUtils.equals(entry.getPath(), MANIFEST_FILE)) {
258 Manifest transformedManifest = getManifestWithoutSlingInitialContentHeader(is);
259 transformedManifest.write(zos);
260 }
261 else {
262 IOUtils.copy(is, zos);
263 }
264 }
265 }
266 }
267 }
268
269 log.info("Created bundle without content: {}", bundleFile.getName());
270 return bundleFile;
271 }
272
273
274
275
276
277
278
279 private Manifest getManifestWithoutSlingInitialContentHeader(InputStream is) throws IOException {
280 Manifest manifest = new Manifest(is);
281 manifest.getMainAttributes().remove(new Attributes.Name(OsgiBundleFile.HEADER_INITIAL_CONTENT));
282 return manifest;
283 }
284
285 }