Skip to content

Commit faec9f4

Browse files
committed
HSEARCH-5356 Introduce the extended (platform) BOM
1 parent 5ed6936 commit faec9f4

File tree

8 files changed

+2771
-23
lines changed

8 files changed

+2771
-23
lines changed

bom/platform-next/pom.xml

Lines changed: 1206 additions & 0 deletions
Large diffs are not rendered by default.

bom/platform/pom.xml

Lines changed: 1201 additions & 0 deletions
Large diffs are not rendered by default.

build/enforcer/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
</properties>
3333

3434
<dependencies>
35+
<dependency>
36+
<groupId>com.google.code.gson</groupId>
37+
<artifactId>gson</artifactId>
38+
</dependency>
3539
<dependency>
3640
<groupId>org.apache.maven.enforcer</groupId>
3741
<artifactId>enforcer-api</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.search.build.enforcer;
6+
7+
import static org.hibernate.search.build.enforcer.MavenProjectUtils.isAnyParentPublicParent;
8+
import static org.hibernate.search.build.enforcer.MavenProjectUtils.isAnyParentRelocationParent;
9+
import static org.hibernate.search.build.enforcer.MavenProjectUtils.isProjectDeploySkipped;
10+
11+
import java.io.IOException;
12+
import java.io.InputStreamReader;
13+
import java.net.URI;
14+
import java.net.URLEncoder;
15+
import java.nio.charset.StandardCharsets;
16+
import java.util.ArrayList;
17+
import java.util.HashSet;
18+
import java.util.List;
19+
import java.util.Locale;
20+
import java.util.Objects;
21+
import java.util.Set;
22+
import java.util.TreeSet;
23+
import java.util.concurrent.TimeUnit;
24+
import java.util.stream.Collectors;
25+
26+
import javax.inject.Inject;
27+
import javax.inject.Named;
28+
29+
import com.google.gson.Gson;
30+
import com.google.gson.GsonBuilder;
31+
import com.google.gson.stream.JsonReader;
32+
33+
import org.apache.maven.enforcer.rule.api.AbstractEnforcerRule;
34+
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
35+
import org.apache.maven.execution.MavenSession;
36+
import org.apache.maven.model.Dependency;
37+
import org.apache.maven.project.MavenProject;
38+
39+
@Named("dependencyManagementIncludesAllGroupIdArtifactsRule") // rule name - must start with lowercase character
40+
public class DependencyManagementIncludesAllGroupIdArtifactsRule extends AbstractEnforcerRule {
41+
/**
42+
* See <a href="https://central.sonatype.org/search/rest-api-guide/">Maven Central REST API</a>
43+
*/
44+
private static final String BASE_URL_FORMAT =
45+
"https://search.maven.org/solrsearch/select?q=%s&core=gav&start=%d&rows=%d&wt=json";
46+
private static final int ROWS_PER_PAGE = 100;
47+
private static final int MAX_RETRIES = 5;
48+
private static final int RETRY_DELAY_SECONDS = 2;
49+
private static final String GAV_FORMAT = "%s:%s:%s";
50+
51+
52+
// Inject needed Maven components
53+
@Inject
54+
private MavenSession session;
55+
56+
/**
57+
* Rule parameter as list of items.
58+
*/
59+
private Set<Dependency> includedExtraDependencies = new HashSet<>();
60+
61+
/**
62+
* Rule parameter as list of items.
63+
*/
64+
private Set<Dependency> dependenciesToSkip = new HashSet<>();
65+
66+
public void execute() throws EnforcerRuleException {
67+
Set<String> dependencies = session.getCurrentProject()
68+
.getDependencyManagement()
69+
.getDependencies()
70+
.stream()
71+
.map( DependencyManagementIncludesAllGroupIdArtifactsRule::dependencyString )
72+
.collect( Collectors.toSet() );
73+
Set<String> skip = dependenciesToSkip.stream()
74+
.map( DependencyManagementIncludesAllGroupIdArtifactsRule::dependencyString )
75+
.collect( Collectors.toSet() );
76+
77+
Set<Artifact> toQuery = new TreeSet<>();
78+
for ( MavenProject project : session.getAllProjects() ) {
79+
if ( skip.contains( String.format( Locale.ROOT, GAV_FORMAT, project.getGroupId(), project.getArtifactId(),
80+
project.getVersion() ) ) ) {
81+
continue;
82+
}
83+
boolean publicParent = isAnyParentPublicParent( project );
84+
boolean relocationParent = isAnyParentRelocationParent( project );
85+
boolean shouldBePublished = publicParent || relocationParent;
86+
boolean deploySkipped = isProjectDeploySkipped( project );
87+
if ( shouldBePublished && !deploySkipped ) {
88+
for ( Dependency dependency : project.getDependencies() ) {
89+
if ( "test".equals( dependency.getScope() ) ) {
90+
continue;
91+
}
92+
toQuery.add( new Artifact( dependency.getGroupId(), null, dependency.getVersion() ) );
93+
}
94+
}
95+
}
96+
97+
for ( Dependency filter : includedExtraDependencies ) {
98+
toQuery.add( new Artifact( filter.getGroupId(), filter.getVersion(), filter.getArtifactId() ) );
99+
}
100+
101+
getLog().info( "Will attempt to resolve the artifacts for the following groups: " + toQuery );
102+
103+
final Gson gson = new GsonBuilder().create();
104+
Set<Artifact> foundArtifacts = new TreeSet<>();
105+
106+
for ( Artifact filter : toQuery ) {
107+
StringBuilder queryBuilder = new StringBuilder();
108+
queryBuilder.append( "g:" ).append( encodeValue( filter.g ) );
109+
110+
if ( filter.a != null && !filter.a.trim().isEmpty() ) {
111+
queryBuilder.append( "+AND+a:" ).append( encodeValue( filter.a ) );
112+
}
113+
if ( filter.v != null && !filter.v.trim().isEmpty() ) {
114+
queryBuilder.append( "+AND+v:" ).append( encodeValue( filter.v ) );
115+
}
116+
117+
int start = 0;
118+
do {
119+
String url = String.format( Locale.ROOT, BASE_URL_FORMAT, queryBuilder, start, ROWS_PER_PAGE );
120+
SearchResponse response = fetch( gson, url );
121+
foundArtifacts.addAll( response.response.docs );
122+
if ( response.response.start + response.response.docs.size() >= response.response.numFound ) {
123+
break;
124+
}
125+
start += ROWS_PER_PAGE;
126+
}
127+
while ( true );
128+
}
129+
130+
List<String> problems = new ArrayList<>();
131+
for ( Artifact artifact : foundArtifacts ) {
132+
String toCheck = artifact.formattedString();
133+
if ( skip.contains( toCheck ) ) {
134+
continue;
135+
}
136+
if ( !dependencies.remove( toCheck ) ) {
137+
// The artifact is NOT in the dependencies
138+
problems.add( "`" + toCheck + "` is missing from the dependency management section" );
139+
}
140+
}
141+
142+
if ( !problems.isEmpty() ) {
143+
throw new EnforcerRuleException( String.join( ";\n", problems ) );
144+
}
145+
}
146+
147+
private static String dependencyString(Dependency d) {
148+
return String.format( Locale.ROOT, GAV_FORMAT, d.getGroupId(), d.getArtifactId(), d.getVersion() );
149+
}
150+
151+
private SearchResponse fetch(Gson gson, String url) throws EnforcerRuleException {
152+
for ( int i = 0; i < MAX_RETRIES; i++ ) {
153+
try (
154+
var stream = URI.create( url ).toURL().openStream(); var isr = new InputStreamReader( stream );
155+
var reader = new JsonReader( isr )
156+
) {
157+
getLog().info( "Fetching included artifacts from " + url );
158+
return gson.fromJson( reader, SearchResponse.class );
159+
}
160+
catch (IOException e) {
161+
getLog().warn( "Fetching artifacts from " + url + " failed Retrying in " + RETRY_DELAY_SECONDS
162+
+ "s... (Attempt " + ( i + 1 ) + "/" + ( MAX_RETRIES ) + "): " + e.getMessage() );
163+
try {
164+
TimeUnit.SECONDS.sleep( RETRY_DELAY_SECONDS );
165+
}
166+
catch (InterruptedException ie) {
167+
Thread.currentThread().interrupt();
168+
throw new EnforcerRuleException( ie );
169+
}
170+
}
171+
}
172+
getLog().warn( "Fetching the artifacts from " + url + " failed after " + ( MAX_RETRIES ) + " attempts." );
173+
return SearchResponse.empty();
174+
}
175+
176+
private String encodeValue(String value) {
177+
return URLEncoder.encode( value, StandardCharsets.UTF_8 );
178+
}
179+
180+
private static class Artifact implements Comparable<Artifact> {
181+
String g;
182+
String a;
183+
String v;
184+
185+
public Artifact(String g, String a, String v) {
186+
this.g = g;
187+
this.a = a;
188+
this.v = v;
189+
}
190+
191+
public Artifact() {
192+
}
193+
194+
public String formattedString() {
195+
return String.format( Locale.ROOT, GAV_FORMAT, g, a, v );
196+
}
197+
198+
@Override
199+
public boolean equals(Object o) {
200+
if ( o == null || getClass() != o.getClass() ) {
201+
return false;
202+
}
203+
Artifact artifact = (Artifact) o;
204+
return Objects.equals( g, artifact.g ) && Objects.equals( a, artifact.a ) && Objects.equals( v, artifact.v );
205+
}
206+
207+
@Override
208+
public int hashCode() {
209+
return Objects.hash( g, a, v );
210+
}
211+
212+
@Override
213+
public int compareTo(Artifact o) {
214+
if ( o == null ) {
215+
return 1;
216+
}
217+
return formattedString().compareTo( o.formattedString() );
218+
}
219+
220+
@Override
221+
public String toString() {
222+
final StringBuilder sb = new StringBuilder();
223+
if ( g != null ) {
224+
sb.append( g );
225+
}
226+
else {
227+
sb.append( "-" );
228+
}
229+
sb.append( ':' );
230+
231+
if ( a != null ) {
232+
sb.append( a );
233+
}
234+
else {
235+
sb.append( "-" );
236+
}
237+
sb.append( ':' );
238+
if ( v != null ) {
239+
sb.append( v );
240+
}
241+
else {
242+
sb.append( "-" );
243+
}
244+
return sb.toString();
245+
}
246+
}
247+
248+
private static class SearchResponse {
249+
ResponseData response;
250+
251+
static SearchResponse empty() {
252+
SearchResponse res = new SearchResponse();
253+
res.response = new ResponseData();
254+
res.response.numFound = 0;
255+
res.response.start = 0;
256+
res.response.docs = List.of();
257+
return res;
258+
}
259+
}
260+
261+
public static class ResponseData {
262+
int numFound;
263+
int start;
264+
List<Artifact> docs;
265+
}
266+
}

build/enforcer/src/main/java/org/hibernate/search/build/enforcer/DependencyManagementIncludesAllPublicArtifactsRule.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.hibernate.search.build.enforcer.MavenProjectUtils.isProjectDeploySkipped;
1010

1111
import java.util.ArrayList;
12+
import java.util.HashSet;
1213
import java.util.List;
1314
import java.util.Set;
1415
import java.util.stream.Collectors;
@@ -21,13 +22,18 @@
2122
import org.apache.maven.execution.MavenSession;
2223
import org.apache.maven.model.Dependency;
2324
import org.apache.maven.project.MavenProject;
25+
import org.apache.maven.project.ProjectDependenciesResolver;
2426

2527
@Named("dependencyManagementIncludesAllPublicArtifactsRule") // rule name - must start with lowercase character
2628
public class DependencyManagementIncludesAllPublicArtifactsRule extends AbstractEnforcerRule {
2729

2830
// Inject needed Maven components
2931
@Inject
3032
private MavenSession session;
33+
@Inject
34+
private ProjectDependenciesResolver projectDependenciesResolver;
35+
36+
private Set<Dependency> excludes = new HashSet<>();
3137

3238
public void execute() throws EnforcerRuleException {
3339
Set<String> dependencies = session.getCurrentProject()
@@ -36,10 +42,16 @@ public void execute() throws EnforcerRuleException {
3642
.stream()
3743
.map( Dependency::getArtifactId )
3844
.collect( Collectors.toSet() );
45+
Set<String> projectsToSkip = excludes.stream()
46+
.map( Dependency::getArtifactId )
47+
.collect( Collectors.toSet() );
3948

4049
List<String> problems = new ArrayList<>();
4150

4251
for ( MavenProject project : session.getAllProjects() ) {
52+
if ( projectsToSkip.contains( project.getArtifactId() ) ) {
53+
continue;
54+
}
4355
boolean publicParent = isAnyParentPublicParent( project );
4456
boolean relocationParent = isAnyParentRelocationParent( project );
4557
boolean shouldBePublished = publicParent || relocationParent;

0 commit comments

Comments
 (0)