24
24
import java .util .concurrent .CompletableFuture ;
25
25
import java .util .concurrent .ConcurrentHashMap ;
26
26
import java .util .concurrent .Future ;
27
+ import java .util .function .Function ;
27
28
import java .util .stream .Collectors ;
28
29
import java .util .stream .Stream ;
29
30
30
31
import org .eclipse .core .runtime .ILog ;
31
32
import org .eclipse .core .runtime .jobs .Job ;
32
33
import org .eclipse .equinox .p2 .metadata .IVersionedId ;
34
+ import org .eclipse .equinox .p2 .metadata .VersionedId ;
33
35
import org .eclipse .osgi .util .NLS ;
34
36
import org .eclipse .pde .internal .genericeditor .target .extension .p2 .Messages ;
35
37
import org .eclipse .pde .internal .genericeditor .target .extension .p2 .P2Fetcher ;
38
+ import org .eclipse .pde .internal .genericeditor .target .extension .p2 .P2Fetcher .RepositoryContent ;
36
39
37
40
/**
38
41
* This class is used to cache the p2 repositories completion information order
@@ -48,7 +51,10 @@ private RepositoryCache() {
48
51
// avoid instantiation
49
52
}
50
53
51
- private static final Map <URI , CompletableFuture <Map <String , List <IVersionedId >>>> CACHE = new ConcurrentHashMap <>();
54
+ private static record RepositoryMetadata (Map <String , List <IVersionedId >> units , List <URI > children ) {
55
+ }
56
+
57
+ private static final Map <URI , CompletableFuture <RepositoryMetadata >> CACHE = new ConcurrentHashMap <>();
52
58
53
59
/**
54
60
* Fetches information and caches it.
@@ -64,36 +70,43 @@ private RepositoryCache() {
64
70
*/
65
71
public static Map <String , List <IVersionedId >> fetchP2UnitsFromRepos (List <String > repositories ) {
66
72
if (repositories .size () == 1 ) {
67
- return getFutureValue (fetchP2DataOfRepo (repositories .get (0 )));
73
+ return getFutureValue (fetchP2DataOfRepo (repositories .get (0 )), RepositoryMetadata :: units , Map . of () );
68
74
}
69
75
var repos = repositories .stream ().map (RepositoryCache ::fetchP2DataOfRepo ).toList ();
70
76
// Fetch all repos at once to await pending metadata in parallel
71
- return toSortedMap (repos .stream ().map (RepositoryCache ::getFutureValue ) //
77
+ return toSortedMap (repos .stream ()
78
+ .map (r -> getFutureValue (r , RepositoryMetadata ::units , Map .<String , List <IVersionedId >>of ()))
72
79
.map (Map ::values ).flatMap (Collection ::stream ).flatMap (List ::stream ));
73
80
}
74
81
82
+ public static List <URI > fetchChildrenOfRepo (String repository ) {
83
+ return getFutureValue (fetchP2DataOfRepo (repository ), RepositoryMetadata ::children , List .of ());
84
+ }
85
+
75
86
public static void prefetchP2MetadataOfRepository (String repository ) {
76
87
fetchP2DataOfRepo (repository );
77
88
}
78
89
79
- private static Future <Map < String , List < IVersionedId >> > fetchP2DataOfRepo (String repository ) {
90
+ private static Future <RepositoryMetadata > fetchP2DataOfRepo (String repository ) {
80
91
URI location ;
81
- try {
82
- location = new URI (repository );
92
+ try { // always have a trailing slash to avoid duplicated cache entries
93
+ location = new URI (repository + ( repository . endsWith ( "/" ) ? "" : "/" ) );
83
94
} catch (URISyntaxException e ) {
84
95
return CompletableFuture .failedFuture (e );
85
96
}
86
97
return CACHE .compute (location , (repo , f ) -> {
87
98
if (f != null && (!f .isDone () || !f .isCompletedExceptionally () && !f .isCancelled ())) {
88
99
return f ; // computation is running or has succeeded
89
100
}
90
- CompletableFuture <Map < String , List < IVersionedId >> > future = new CompletableFuture <>();
101
+ CompletableFuture <RepositoryMetadata > future = new CompletableFuture <>();
91
102
// Fetching P2 repository information is a costly operation
92
103
// time-wise. Thus it is done in a job.
93
104
Job job = Job .create (NLS .bind (Messages .UpdateJob_P2DataFetch , repo ), m -> {
94
105
try {
95
- Map <String , List <IVersionedId >> units = toSortedMap (P2Fetcher .fetchAvailableUnits (repo , m ));
96
- future .complete (units );
106
+ RepositoryContent content = P2Fetcher .fetchAvailableUnits (repo , m );
107
+ Map <String , List <IVersionedId >> units = toSortedMap (
108
+ content .units ().stream ().map (iu -> new VersionedId (iu .getId (), iu .getVersion ())));
109
+ future .complete (new RepositoryMetadata (units , content .children ()));
97
110
} catch (Throwable e ) {
98
111
future .completeExceptionally (e );
99
112
// Only log the failure, don't open an error-dialog.
@@ -115,11 +128,12 @@ private static Map<String, List<IVersionedId>> toSortedMap(Stream<IVersionedId>
115
128
Collectors .groupingBy (IVersionedId ::getId , LinkedHashMap ::new , Collectors .toUnmodifiableList ()));
116
129
}
117
130
118
- private static Map <String , List <IVersionedId >> getFutureValue (Future <Map <String , List <IVersionedId >>> future ) {
131
+ private static <T > T getFutureValue (Future <RepositoryMetadata > future , Function <RepositoryMetadata , T > getter ,
132
+ T defaultValue ) {
119
133
try {
120
- return future .get ();
134
+ return getter . apply ( future .get () );
121
135
} catch (Exception e ) { // interrupted, canceled or execution failure
122
- return Map . of () ;
136
+ return defaultValue ;
123
137
}
124
138
}
125
139
0 commit comments