Skip to content

Package: AboutScanner

AboutScanner

nameinstructionbranchcomplexitylinemethod
AboutScanner(Stream)
M: 12 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
getEntries()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
lambda$processHtmlFile$0(String, String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
lambda$scan$1(AboutEntry)
M: 6 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
map(URL)
M: 110 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 22 C: 0
0%
M: 1 C: 0
0%
needAbout(AboutEntry)
M: 8 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
parseOsgiLicense(String)
M: 30 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
processHtmlFile(AboutEntry, URL, InputStream)
M: 35 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
processLicense(AboutEntry, URL, InputStream)
M: 29 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
processMavenProperties(AboutEntry, ZipEntry, InputStream)
M: 70 C: 0
0%
M: 14 C: 0
0%
M: 8 C: 0
0%
M: 16 C: 0
0%
M: 1 C: 0
0%
processNotice(AboutEntry, URL, InputStream)
M: 18 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
processOsgiManifest(AboutEntry, ZipEntry, InputStream)
M: 67 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 16 C: 0
0%
M: 1 C: 0
0%
processPlainManifest(AboutEntry, URL, InputStream)
M: 47 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 14 C: 0
0%
M: 1 C: 0
0%
scan()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
scan(Stream)
M: 9 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setIdFromUrl(AboutEntry, URL)
M: 40 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2017, 2022 Red Hat Inc and others.
3: *
4: * This program and the accompanying materials are made
5: * available under the terms of the Eclipse Public License 2.0
6: * which is available at https://www.eclipse.org/legal/epl-2.0/
7: *
8: * SPDX-License-Identifier: EPL-2.0
9: *
10: * Contributors:
11: * Red Hat Inc - initial API and implementation
12: * Arthur Deschamps - EPL license detection
13: *******************************************************************************/
14: package org.eclipse.kapua.commons.about;
15:
16: import com.google.common.io.CharStreams;
17: import org.eclipse.kapua.commons.about.AboutEntry.License;
18: import org.reflections.util.ClasspathHelper;
19: import org.slf4j.Logger;
20: import org.slf4j.LoggerFactory;
21:
22: import java.io.IOException;
23: import java.io.InputStream;
24: import java.io.InputStreamReader;
25: import java.net.URL;
26: import java.nio.charset.StandardCharsets;
27: import java.util.Arrays;
28: import java.util.Comparator;
29: import java.util.List;
30: import java.util.Properties;
31: import java.util.jar.Manifest;
32: import java.util.stream.Collectors;
33: import java.util.stream.Stream;
34: import java.util.zip.ZipEntry;
35: import java.util.zip.ZipInputStream;
36:
37: public final class AboutScanner {
38:
39: private static final Logger LOG = LoggerFactory.getLogger(AboutScanner.class);
40:
41: private final List<AboutEntry> entries;
42:
43: private AboutScanner(final Stream<AboutEntry> entries) {
44: this.entries = entries.sorted(Comparator.comparing(AboutEntry::getId)).collect(Collectors.toList());
45: }
46:
47: public List<AboutEntry> getEntries() {
48: return entries;
49: }
50:
51: public static AboutScanner scan() {
52: return scan(ClasspathHelper.forClassLoader().stream());
53: }
54:
55: private static AboutEntry map(URL url) {
56:
57: AboutEntry result = null;
58:
59: try (InputStream in = url.openStream();
60: ZipInputStream zis = new ZipInputStream(in)) {
61:
62: ZipEntry entry;
63:• while ((entry = zis.getNextEntry()) != null) {
64:• if ("META-INF/MANIFEST.MF".equals(entry.getName())) {
65: result = processOsgiManifest(result, entry, zis);
66: result = processPlainManifest(result, url, zis);
67:• } else if (entry.getName().startsWith("META-INF/maven/") && entry.getName().endsWith("/pom.properties")) {
68: result = processMavenProperties(result, entry, zis);
69:• } else if ("META-INF/NOTICE.txt".equals(entry.getName())) {
70: result = processNotice(result, url, zis);
71:• } else if ("META-INF/NOTICE".equals(entry.getName())) {
72: result = processNotice(result, url, zis);
73:• } else if ("about.html".equals(entry.getName())) {
74: result = processHtmlFile(result, url, zis);
75:• } else if ("META-INF/LICENSE.txt".equals(entry.getName())) {
76: result = processLicense(result, url, zis);
77:• } else if ("META-INF/LICENSE".equals(entry.getName())) {
78: result = processLicense(result, url, zis);
79: }
80: }
81: } catch (IOException e) {
82: // ignore
83: }
84:
85: return result;
86: }
87:
88: private static AboutEntry processPlainManifest(AboutEntry about, final URL url, final InputStream in) {
89: try {
90: final Manifest mf = new Manifest(in);
91: final String name = mf.getMainAttributes().getValue("Specification-Title");
92: final String version = mf.getMainAttributes().getValue("Specification-Version");
93:
94:• if (name == null || version == null) {
95: return about;
96: }
97:
98: about = needAbout(about);
99: setIdFromUrl(about, url);
100:
101:• if (about.getName() == null && about.getVersion() == null) {
102: about.setName(name);
103: about.setVersion(version);
104: }
105:
106: } catch (Exception e) {
107: LOG.debug("Problem during archive processing:", e);
108: }
109: return about;
110: }
111:
112: /**
113: * Detect if the underlying project is EPL licensed
114: */
115: private static AboutEntry processHtmlFile(AboutEntry about, final URL url, final InputStream in) {
116:
117: about = processNotice(about, url, in);
118:
119: final List<String> keywords = Arrays.asList("epl", "eclipse public license");
120: final String fileContent = about.getNotice();
121:
122: // Makes sure all the keywords are contained in the about.html file
123:• if (keywords.stream().allMatch(keyword -> fileContent.toLowerCase().contains(keyword.toLowerCase()))) {
124: about.setLicense(License.EPL);
125: } else {
126: // Default license
127: about.setLicense(License.UNKNOWN);
128: }
129:
130: return about;
131: }
132:
133: /**
134: * Parse an Apache NOTICE file
135: */
136: private static AboutEntry processNotice(AboutEntry about, final URL url, final InputStream in) {
137:
138: about = needAbout(about);
139:
140: setIdFromUrl(about, url);
141:
142: try {
143: about.setNotice(CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)));
144: } catch (Exception e) {
145: }
146:
147: return about;
148: }
149:
150: /**
151: * Parse Apache LICENSE files
152: */
153: private static AboutEntry processLicense(AboutEntry about, final URL url, final InputStream in) {
154:
155: about = needAbout(about);
156:
157: setIdFromUrl(about, url);
158:
159: try {
160: final String text = CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8));
161:• if (about.getLicense() == License.UNKNOWN) {
162: // only set the text of we don't have any more precise information
163: about.setLicense(new License(null, text, null));
164: }
165: } catch (Exception e) {
166: }
167:
168: return about;
169: }
170:
171: private static void setIdFromUrl(AboutEntry about, final URL url) {
172:• if (about.getId() == null) {
173: final String[] paths = url.getPath().split("/");
174:• if (paths.length < 1) {
175: // set the full URL, we don't understand it
176: about.setId("url:" + url.toString());
177: } else {
178: // set URL based id
179: about.setId("url:" + paths[paths.length - 1]);
180: }
181: }
182: }
183:
184: private static AboutEntry processMavenProperties(AboutEntry about, final ZipEntry entry, final InputStream in) {
185: try {
186: final Properties p = new Properties();
187: p.load(in);
188:
189: final String groupId = p.getProperty("groupId");
190: final String artifactId = p.getProperty("artifactId");
191: final String version = p.getProperty("version");
192:
193:• if (groupId != null && artifactId != null && version != null) {
194:
195: about = needAbout(about);
196:
197: // check if an ID is already present, maven goes last
198:• if (about.getId() == null || about.getId().startsWith("url:")) {
199: about.setId(String.format("mvn:%s:%s:%s", groupId, artifactId, version));
200: }
201:
202:• if (about.getVersion() == null) {
203: about.setVersion(version);
204: }
205:• if (about.getName() == null) {
206: about.setName(artifactId);
207: }
208: }
209: } catch (Exception e) {
210: }
211: return about;
212: }
213:
214: private static AboutEntry processOsgiManifest(AboutEntry about, final ZipEntry entry, final InputStream in) {
215: try {
216: final Manifest mf = new Manifest(in);
217: final String mfv = mf.getMainAttributes().getValue("Bundle-ManifestVersion");
218:• if (!"2".equals(mfv)) {
219: return about;
220: }
221:
222: // OSGi
223:
224: final String bsn = mf.getMainAttributes().getValue("Bundle-SymbolicName");
225: final String version = mf.getMainAttributes().getValue("Bundle-Version");
226: final String name = mf.getMainAttributes().getValue("Bundle-Name");
227: final String licenseString = mf.getMainAttributes().getValue("Bundle-License");
228:
229: about = needAbout(about);
230:
231: about.setId(String.format("osgi:%s:%s", bsn, version));
232: about.setName(name);
233: about.setVersion(version);
234: about.setLicense(parseOsgiLicense(licenseString));
235:
236: } catch (Exception e) {
237: }
238: return about;
239: }
240:
241: private static License parseOsgiLicense(final String string) {
242:• if (string == null || string.isEmpty()) {
243: return License.UNKNOWN;
244: }
245:
246:• if (string.equals(License.APL2.getUrl().toString())) {
247: return License.APL2;
248: }
249:
250: try {
251: URL url = new URL(string);
252: return new License(null, null, url);
253: } catch (Exception e) {
254: }
255:
256: return License.UNKNOWN;
257: }
258:
259: private static AboutEntry needAbout(AboutEntry about) {
260:• if (about != null) {
261: return about;
262: } else {
263: return new AboutEntry();
264: }
265: }
266:
267: public static AboutScanner scan(final Stream<URL> urls) {
268:• return new AboutScanner(urls.map(AboutScanner::map).filter(entry -> entry != null));
269: }
270: }