Skip to content

Commit b51821a

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

File tree

8 files changed

+2770
-23
lines changed

8 files changed

+2770
-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,265 @@
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+
50+
51+
// Inject needed Maven components
52+
@Inject
53+
private MavenSession session;
54+
55+
/**
56+
* Rule parameter as list of items.
57+
*/
58+
private Set<Dependency> includedExtraDependencies = new HashSet<>();
59+
60+
/**
61+
* Rule parameter as list of items.
62+
*/
63+
private Set<Dependency> dependenciesToSkip = new HashSet<>();
64+
65+
public void execute() throws EnforcerRuleException {
66+
Set<String> dependencies = session.getCurrentProject()
67+
.getDependencyManagement()
68+
.getDependencies()
69+
.stream()
70+
.map( DependencyManagementIncludesAllGroupIdArtifactsRule::dependencyString )
71+
.collect( Collectors.toSet() );
72+
Set<String> skip = dependenciesToSkip.stream()
73+
.map( DependencyManagementIncludesAllGroupIdArtifactsRule::dependencyString )
74+
.collect( Collectors.toSet() );
75+
76+
Set<Artifact> toQuery = new TreeSet<>();
77+
for ( MavenProject project : session.getAllProjects() ) {
78+
if ( skip.contains( String.format( Locale.ROOT, "%s:%s:%s", project.getGroupId(), project.getArtifactId(),
79+
project.getVersion() ) ) ) {
80+
continue;
81+
}
82+
boolean publicParent = isAnyParentPublicParent( project );
83+
boolean relocationParent = isAnyParentRelocationParent( project );
84+
boolean shouldBePublished = publicParent || relocationParent;
85+
boolean deploySkipped = isProjectDeploySkipped( project );
86+
if ( shouldBePublished && !deploySkipped ) {
87+
for ( Dependency dependency : project.getDependencies() ) {
88+
if ( "test".equals( dependency.getScope() ) ) {
89+
continue;
90+
}
91+
toQuery.add( new Artifact( dependency.getGroupId(), null, dependency.getVersion() ) );
92+
}
93+
}
94+
}
95+
96+
for ( Dependency filter : includedExtraDependencies ) {
97+
toQuery.add( new Artifact( filter.getGroupId(), filter.getVersion(), filter.getArtifactId() ) );
98+
}
99+
100+
getLog().info( "Will attempt to resolve the artifacts for the following groups: " + toQuery );
101+
102+
final Gson gson = new GsonBuilder().create();
103+
Set<Artifact> foundArtifacts = new TreeSet<>();
104+
105+
for ( Artifact filter : toQuery ) {
106+
StringBuilder queryBuilder = new StringBuilder();
107+
queryBuilder.append( "g:" ).append( encodeValue( filter.g ) );
108+
109+
if ( filter.a != null && !filter.a.trim().isEmpty() ) {
110+
queryBuilder.append( "+AND+a:" ).append( encodeValue( filter.a ) );
111+
}
112+
if ( filter.v != null && !filter.v.trim().isEmpty() ) {
113+
queryBuilder.append( "+AND+v:" ).append( encodeValue( filter.v ) );
114+
}
115+
116+
int start = 0;
117+
do {
118+
String url = String.format( Locale.ROOT, BASE_URL_FORMAT, queryBuilder, start, ROWS_PER_PAGE );
119+
SearchResponse response = fetch( gson, url );
120+
foundArtifacts.addAll( response.response.docs );
121+
if ( response.response.start + response.response.docs.size() >= response.response.numFound ) {
122+
break;
123+
}
124+
start += ROWS_PER_PAGE;
125+
}
126+
while ( true );
127+
}
128+
129+
List<String> problems = new ArrayList<>();
130+
for ( Artifact artifact : foundArtifacts ) {
131+
String toCheck = artifact.formattedString();
132+
if ( skip.contains( toCheck ) ) {
133+
continue;
134+
}
135+
if ( !dependencies.remove( toCheck ) ) {
136+
// The artifact is NOT in the dependencies
137+
problems.add( "`" + toCheck + "` is missing from the dependency management section" );
138+
}
139+
}
140+
141+
if ( !problems.isEmpty() ) {
142+
throw new EnforcerRuleException( String.join( ";\n", problems ) );
143+
}
144+
}
145+
146+
private static String dependencyString(Dependency d) {
147+
return String.format( Locale.ROOT, "%s:%s:%s", d.getGroupId(), d.getArtifactId(), d.getVersion() );
148+
}
149+
150+
private SearchResponse fetch(Gson gson, String url) throws EnforcerRuleException {
151+
for ( int i = 0; i < MAX_RETRIES; i++ ) {
152+
try (
153+
var stream = URI.create( url ).toURL().openStream(); var isr = new InputStreamReader( stream );
154+
var reader = new JsonReader( isr )
155+
) {
156+
getLog().info( "Fetching included artifacts from " + url );
157+
return gson.fromJson( reader, SearchResponse.class );
158+
}
159+
catch (IOException e) {
160+
getLog().warn( "Fetching artifacts from " + url + " failed Retrying in " + RETRY_DELAY_SECONDS
161+
+ "s... (Attempt " + ( i + 1 ) + "/" + ( MAX_RETRIES ) + "): " + e.getMessage() );
162+
try {
163+
TimeUnit.SECONDS.sleep( RETRY_DELAY_SECONDS );
164+
}
165+
catch (InterruptedException ie) {
166+
Thread.currentThread().interrupt();
167+
throw new EnforcerRuleException( ie );
168+
}
169+
}
170+
}
171+
getLog().warn( "Fetching the artifacts from " + url + " failed after " + ( MAX_RETRIES ) + " attempts." );
172+
return SearchResponse.empty();
173+
}
174+
175+
private String encodeValue(String value) {
176+
return URLEncoder.encode( value, StandardCharsets.UTF_8 );
177+
}
178+
179+
private static class Artifact implements Comparable<Artifact> {
180+
String g;
181+
String a;
182+
String v;
183+
184+
public Artifact(String g, String a, String v) {
185+
this.g = g;
186+
this.a = a;
187+
this.v = v;
188+
}
189+
190+
public Artifact() {
191+
}
192+
193+
public String formattedString() {
194+
return String.format( Locale.ROOT, "%s:%s:%s", g, a, v );
195+
}
196+
197+
@Override
198+
public boolean equals(Object o) {
199+
if ( o == null || getClass() != o.getClass() ) {
200+
return false;
201+
}
202+
Artifact artifact = (Artifact) o;
203+
return Objects.equals( g, artifact.g ) && Objects.equals( a, artifact.a ) && Objects.equals( v, artifact.v );
204+
}
205+
206+
@Override
207+
public int hashCode() {
208+
return Objects.hash( g, a, v );
209+
}
210+
211+
@Override
212+
public int compareTo(Artifact o) {
213+
if ( o == null ) {
214+
return 1;
215+
}
216+
return formattedString().compareTo( o.formattedString() );
217+
}
218+
219+
@Override
220+
public String toString() {
221+
final StringBuffer sb = new StringBuffer();
222+
if ( g != null ) {
223+
sb.append( g );
224+
}
225+
else {
226+
sb.append( "-" );
227+
}
228+
sb.append( ':' );
229+
230+
if ( a != null ) {
231+
sb.append( a );
232+
}
233+
else {
234+
sb.append( "-" );
235+
}
236+
sb.append( ':' );
237+
if ( v != null ) {
238+
sb.append( v );
239+
}
240+
else {
241+
sb.append( "-" );
242+
}
243+
return sb.toString();
244+
}
245+
}
246+
247+
private static class SearchResponse {
248+
ResponseData response;
249+
250+
static SearchResponse empty() {
251+
SearchResponse res = new SearchResponse();
252+
res.response = new ResponseData();
253+
res.response.numFound = 0;
254+
res.response.start = 0;
255+
res.response.docs = List.of();
256+
return res;
257+
}
258+
}
259+
260+
public static class ResponseData {
261+
int numFound;
262+
int start;
263+
List<Artifact> docs;
264+
}
265+
}

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)