1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.creadur.tentacles;
18
19 import static org.apache.creadur.tentacles.LicenseType.loadLicensesFrom;
20 import static org.apache.creadur.tentacles.RepositoryType.HTTP;
21 import static org.apache.creadur.tentacles.RepositoryType.LOCAL_FILE_SYSTEM;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.net.URI;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.zip.ZipEntry;
35 import java.util.zip.ZipInputStream;
36
37 import org.apache.logging.log4j.*;
38
39 public class Main {
40
41 static {
42
43
44
45
46
47
48 }
49
50 private static final Logger log = LogManager.getLogger(Main.class);
51 private static final String CRAWL_PATTERN = ".*\\.(jar|zip|war|ear|rar|tar.gz)";
52
53 private final Reports reports;
54 private final Licenses licenses;
55
56 private final Layout layout;
57 private final Platform platform;
58 private final Configuration configuration;
59 private final FileSystem fileSystem;
60 private final IOSystem ioSystem;
61 private final TentaclesResources tentaclesResources;
62 private final Templates templates;
63
64 public Main(final String... args) throws Exception {
65 this(new Configuration(args), Platform.aPlatform());
66 }
67
68 public Main(final Configuration configuration, final Platform platform)
69 throws Exception {
70 this(configuration, platform, new Templates(platform), new Layout(
71 platform, configuration));
72 }
73
74 public Main(final Configuration configuration, final Platform platform,
75 final Templates templates, final Layout layout) throws Exception {
76 this.platform = platform;
77 this.configuration = configuration;
78 this.layout = layout;
79 this.fileSystem = platform.getFileSystem();
80 this.ioSystem = platform.getIoSystem();
81 this.tentaclesResources = platform.getTentaclesResources();
82 this.templates = templates;
83
84 this.reports = new Reports();
85
86 log.info("Remote repository: "
87 + this.configuration.getStagingRepositoryURI());
88 log.info("Local root directory: " + this.layout.getLocalRootDirectory());
89
90 this.tentaclesResources.copyTo("legal/style.css",
91 new File(this.layout.getOutputDirectory(), "style.css"));
92
93 this.licenses = loadLicensesFrom(platform);
94 }
95
96 public static void main(final String[] args) throws Exception {
97
98 log.info("Launching Apache Tentacles ...");
99
100 if(args == null || args.length < 1) {
101 log.error("Error: Input parameter missing - you did not specify any component to run Apache Tentacles on.");
102 log.error("Please launch Apache Tentacles with an URI to work on such as 'https://repository.apache.org/content/repositories/orgapachecreadur-1000/'.");
103 } else {
104 new Main(args).main();
105 }
106
107 }
108
109 private void main() throws Exception {
110
111 unpackContents(mirrorRepositoryFrom(this.configuration));
112
113 reportOn(archivesIn(this.layout.getRepositoryDirectory()));
114 }
115
116 private List<Archive> archivesIn(final File repository) {
117 final List<File> jars = this.fileSystem.documentsFrom(repository);
118
119 final List<Archive> archives = new ArrayList<Archive>();
120 for (final File file : jars) {
121 final Archive archive =
122 new Archive(file, this.fileSystem, this.layout);
123 archives.add(archive);
124 }
125 return archives;
126 }
127
128 private void reportOn(final List<Archive> archives) throws IOException {
129 this.templates
130 .template("legal/archives.vm")
131 .add("archives", archives)
132 .add("reports", this.reports)
133 .write(new File(this.layout.getOutputDirectory(),
134 "archives.html"));
135
136 reportLicenses(archives);
137 reportNotices(archives);
138 reportDeclaredLicenses(archives);
139 reportDeclaredNotices(archives);
140 }
141
142 private void reportLicenses(final List<Archive> archives)
143 throws IOException {
144 initLicenses(archives);
145
146 this.templates
147 .template("legal/licenses.vm")
148 .add("licenses", getLicenses(archives))
149 .add("reports", this.reports)
150 .write(new File(this.layout.getOutputDirectory(),
151 "licenses.html"));
152 }
153
154 private void initLicenses(final List<Archive> archives) throws IOException {
155 final Map<License, License> licenses = new HashMap<License, License>();
156
157 for (final Archive archive : archives) {
158 final List<File> files =
159 this.fileSystem.licensesFrom(archive.contentsDirectory());
160 for (final File file : files) {
161 final License license = this.licenses.from(file);
162
163 License existing = licenses.get(license);
164 if (existing == null) {
165 licenses.put(license, license);
166 existing = license;
167 }
168
169 existing.getLocations().add(file);
170 existing.getArchives().add(archive);
171 archive.getLicenses().add(existing);
172 }
173 }
174 }
175
176 private Collection<License> getLicenses(final List<Archive> archives) {
177 final Set<License> licenses = new LinkedHashSet<License>();
178 for (final Archive archive : archives) {
179 licenses.addAll(archive.getLicenses());
180 }
181 return licenses;
182 }
183
184 private void reportDeclaredLicenses(final List<Archive> archives)
185 throws IOException {
186
187 for (final Archive archive : archives) {
188
189 classifyLicenses(archive);
190 }
191 for (final Archive archive : archives) {
192
193 this.templates
194 .template("legal/archive-licenses.vm")
195 .add("archive", archive)
196 .add("reports", this.reports)
197 .write(new File(this.layout.getOutputDirectory(),
198 this.reports.licenses(archive)));
199 }
200
201 }
202
203 private void classifyLicenses(final Archive archive) throws IOException {
204 final Set<License> undeclared =
205 new HashSet<License>(archive.getLicenses());
206
207 final File contents = archive.contentsDirectory();
208 final List<File> files = this.fileSystem.licensesDeclaredIn(contents);
209
210 for (final File file : files) {
211
212 undeclared.remove(this.licenses.from(file));
213
214 }
215
216 archive.getOtherLicenses().addAll(undeclared);
217
218 final Set<License> declared =
219 new HashSet<License>(archive.getLicenses());
220 declared.removeAll(undeclared);
221 archive.getDeclaredLicenses().addAll(declared);
222
223 for (final License license : undeclared) {
224
225 for (final License declare : declared) {
226 if (license.implies(declare)) {
227 archive.getOtherLicenses().remove(license);
228 }
229 }
230 }
231 }
232
233 private void reportDeclaredNotices(final List<Archive> archives)
234 throws IOException {
235
236 for (final Archive archive : archives) {
237
238 final Set<Notice> undeclared =
239 new HashSet<Notice>(archive.getNotices());
240
241 final File contents = archive.contentsDirectory();
242 final List<File> files =
243 this.fileSystem.noticesDeclaredIn(contents);
244
245 for (final File file : files) {
246
247 final Notice notice = new Notice(this.ioSystem.slurp(file));
248
249 undeclared.remove(notice);
250 }
251
252 archive.getOtherNotices().addAll(undeclared);
253
254 final Set<Notice> declared =
255 new HashSet<Notice>(archive.getNotices());
256 declared.removeAll(undeclared);
257 archive.getDeclaredNotices().addAll(declared);
258
259 for (final Notice notice : undeclared) {
260
261 for (final Notice declare : declared) {
262 if (notice.implies(declare)) {
263 archive.getOtherLicenses().remove(notice);
264 }
265 }
266 }
267
268 this.templates
269 .template("legal/archive-notices.vm")
270 .add("archive", archive)
271 .add("reports", this.reports)
272 .write(new File(this.layout.getOutputDirectory(),
273 this.reports.notices(archive)));
274 }
275 }
276
277 private void reportNotices(final List<Archive> archives) throws IOException {
278 final Map<Notice, Notice> notices = new HashMap<Notice, Notice>();
279
280 for (final Archive archive : archives) {
281 final List<File> noticeDocuments =
282 this.fileSystem.noticesOnly(archive.contentsDirectory());
283 for (final File file : noticeDocuments) {
284 final Notice notice = new Notice(this.ioSystem.slurp(file));
285
286 Notice existing = notices.get(notice);
287 if (existing == null) {
288 notices.put(notice, notice);
289 existing = notice;
290 }
291
292 existing.getLocations().add(file);
293 existing.getArchives().add(archive);
294 archive.getNotices().add(existing);
295 }
296 }
297
298 this.templates
299 .template("legal/notices.vm")
300 .add("notices", notices.values())
301 .add("reports", this.reports)
302 .write(new File(this.layout.getOutputDirectory(),
303 "notices.html"));
304 }
305
306 private void unpackContents(final Set<File> files) throws IOException {
307 for (final File file : files) {
308 unpack(file);
309 }
310 }
311
312 private Set<File> mirrorRepositoryFrom(final Configuration configuration)
313 throws IOException {
314 final Set<File> files = new HashSet<File>();
315 if (HTTP.isRepositoryFor(configuration)) {
316 final NexusClient client = new NexusClient(this.platform);
317 final Set<URI> resources =
318 client.crawl(configuration.getStagingRepositoryURI());
319
320 for (final URI uri : resources) {
321 if (!uri.getPath().matches(CRAWL_PATTERN)) {
322 continue;
323 }
324 files.add(client.download(uri, mirroredFrom(uri)));
325 }
326 } else if (LOCAL_FILE_SYSTEM.isRepositoryFor(configuration)) {
327 final File file = new File(configuration.getStagingRepositoryURI());
328 final List<File> collect =
329 this.platform.getFileSystem().archivesInPath(file,
330 configuration.getFileRepositoryPathNameFilter());
331
332 for (final File f : collect) {
333 files.add(copyToMirror(f));
334 }
335 }
336 return files;
337 }
338
339 private void unpack(final File archive) throws IOException {
340 log.info("Unpack " + archive);
341
342 try {
343 final ZipInputStream zip = this.ioSystem.unzip(archive);
344
345 final File contents =
346 new Archive(archive, this.fileSystem, this.layout)
347 .contentsDirectory();
348
349 try {
350 ZipEntry entry = null;
351
352 while ((entry = zip.getNextEntry()) != null) {
353
354 if (entry.isDirectory()) {
355 continue;
356 }
357
358 final String path = entry.getName();
359
360 final File fileEntry = new File(contents, path);
361
362 this.fileSystem.mkparent(fileEntry);
363
364
365
366 this.ioSystem.copy(zip, fileEntry);
367
368 if (fileEntry.getName().endsWith(".jar")) {
369 unpack(fileEntry);
370 }
371 }
372 } finally {
373 this.ioSystem.close(zip);
374 }
375 } catch (final IOException e) {
376 log.error("Not a zip " + archive);
377 }
378 }
379
380 private File copyToMirror(final File src) throws IOException {
381 final URI uri = src.toURI();
382
383 final File file = mirroredFrom(uri);
384
385 log.info("Copy " + uri);
386
387 this.fileSystem.mkparent(file);
388
389 this.ioSystem.copy(this.ioSystem.read(src), file);
390
391 return file;
392 }
393
394 private File mirroredFrom(final URI uri) {
395 final String name =
396 uri.toString()
397 .replace(
398 this.configuration.getStagingRepositoryURI()
399 .toString(), "").replaceFirst("^/", "");
400 return new File(this.layout.getRepositoryDirectory(), name);
401 }
402
403 }