-
Notifications
You must be signed in to change notification settings - Fork 32
/
ClearlyDefinedSupport.java
150 lines (134 loc) · 5.54 KB
/
ClearlyDefinedSupport.java
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*************************************************************************
* Copyright (c) 2019,2021 The Eclipse Foundation and others.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
* distribution, and is available at https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*************************************************************************/
package org.eclipse.dash.licenses.clearlydefined;
import java.io.StringReader;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.eclipse.dash.licenses.IContentData;
import org.eclipse.dash.licenses.IContentId;
import org.eclipse.dash.licenses.ILicenseDataProvider;
import org.eclipse.dash.licenses.LicenseSupport;
import org.eclipse.dash.licenses.LicenseSupport.Status;
import org.eclipse.dash.licenses.context.IContext;
import org.eclipse.dash.licenses.util.JsonUtils;
import com.google.common.flogger.FluentLogger;
public class ClearlyDefinedSupport implements ILicenseDataProvider {
private static final FluentLogger log = FluentLogger.forEnclosingClass();
private IContext context;
public ClearlyDefinedSupport(IContext context) {
this.context = context;
}
/**
* The ClearlyDefined API expects a flat array of ids in JSON format in the
* payload of the POST request.
*
* <pre>
* {
* "maven/mavencentral/io.netty/netty-transport/4.1.42",
* "maven/mavencentral/io.netty/netty-resolver/4.1.42",
* ...
* }
* </pre>
*
* And answers an associative array in JSON format similar to the following.
*
* <pre>
* {
* "maven/mavencentral/io.netty/netty-transport/4.1.42":{ ... },
* "maven/mavencentral/io.netty/netty-resolver/4.1.42":{ ... },
* ...
* }
* </pre>
*
* See the {@link ClearlyDefinedSupport} type for an example of the value.
*
* Note that the ClearlyDefined API will always return a value for every id that
* is provided. In cases where the id is not in the ClearlyDefined database, the
* score is reported as 0. This implementation will only pass those values that
* have a score greater than the confidence threshold (as specified by the
* settings); other values are ignored.
*
* @param ids ids to search in five-part ClearlyDefined format.
* @param consumer the closure to execute with a instance of
* {@link ClearlyDefinedContentData} for each value included in
* the result.
*/
@Override
public void queryLicenseData(Collection<IContentId> ids, Consumer<IContentData> consumer) {
/*
* Only ask ClearlyDefined for information about content that we know that it
* may actually have an answer for.
*/
List<IContentId> filteredIds = ids.stream().filter(id -> isSupported(id)).collect(Collectors.toList());
if (filteredIds.isEmpty())
return;
log.atInfo().log("Querying ClearlyDefined for license data for %1$d items.", filteredIds.size());
int code = context.getHttpClientService().post(context.getSettings().getClearlyDefinedDefinitionsUrl(),
"application/json", JsonUtils.toJson(filteredIds), response -> {
// FIXME Seems like overkill.
AtomicInteger counter = new AtomicInteger();
JsonUtils.readJson(new StringReader(response)).forEach((key, each) -> {
ClearlyDefinedContentData data = new ClearlyDefinedContentData(key, each.asJsonObject());
data.setStatus(isAccepted(data) ? Status.Approved : Status.Restricted);
consumer.accept(data);
counter.incrementAndGet();
});
log.atInfo().log("Found %1$d items.", counter.get());
});
if (code != 200)
log.atSevere().log("ClearlyDefined data search time out; maybe decrease batch size.");
}
/**
* Answers whether or not this id is supported by ClearlyDefined.
*
* @param id
* @return
*/
private boolean isSupported(IContentId id) {
/*
* HACK: ClearlyDefined throws an error when we send it types or sources that it
* doesn't recognise. So let's avoid doing that. Since we don't have a means of
* knowing what types and sources are supported, we take an approach of
* identifying those items that we know aren't supported.
*/
return !"p2".equals(id.getType());
}
/**
* Answers whether or not an entry retrieved from ClearlyDefined should be
* accepted. This is <code>true</code> when the score is not below the threshold
* (as defined by the settings) and all of the discovered licenses are in the
* Eclipse Foundation acceptable licenses list. Answers <code>false</code>
* otherwise.
*
* @see LicenseSupport
* @see ClearlyDefinedContentData
*
* @param data An instance of {@link ClearlyDefinedContentData} representing one
* row from the results.
* @return <code>true</code> when the data is acceptable, <code>false</code>
* otherwise.
*/
public boolean isAccepted(ClearlyDefinedContentData data) {
if (data.getEffectiveScore() >= context.getSettings().getConfidenceThreshold()
|| data.getLicenseScore() >= context.getSettings().getConfidenceThreshold()) {
if (context.getLicenseService().getStatus(data.getLicense()) != LicenseSupport.Status.Approved)
return false;
return !data.discoveredLicenses().filter(license -> !"NONE".equals(license))
.filter(license -> !isDiscoveredLicenseApproved(license)).findAny().isPresent();
}
return false;
}
boolean isDiscoveredLicenseApproved(String license) {
return context.getLicenseService().getStatus(license) == LicenseSupport.Status.Approved;
}
}