Merge pull request #21 from ThomasRubini/async
This commit is contained in:
commit
84a9778944
36
src/main/java/fr/packageviewer/Pair.java
Normal file
36
src/main/java/fr/packageviewer/Pair.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package fr.packageviewer;
|
||||||
|
|
||||||
|
public class Pair<K,V> {
|
||||||
|
private K first;
|
||||||
|
private V second;
|
||||||
|
|
||||||
|
public Pair() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair(K first, V second) {
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K getFirst() {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirst(K first) {
|
||||||
|
this.first = first;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V getSecond() {
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecond(V second) {
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Pair{key=%s,value=%s}".formatted(first, second);
|
||||||
|
}
|
||||||
|
}
|
@ -2,37 +2,69 @@ package fr.packageviewer.distribution;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import java.net.http.*;
|
import java.net.http.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import fr.packageviewer.LoggerManager;
|
import fr.packageviewer.LoggerManager;
|
||||||
|
import fr.packageviewer.Pair;
|
||||||
import fr.packageviewer.pack.Package;
|
import fr.packageviewer.pack.Package;
|
||||||
import fr.packageviewer.pack.SearchedPackage;
|
import fr.packageviewer.pack.SearchedPackage;
|
||||||
|
import fr.packageviewer.parser.AsyncRequestsParser;
|
||||||
import org.json.*;
|
import org.json.*;
|
||||||
|
|
||||||
public class ArchDistribution implements Distribution {
|
public class ArchDistribution extends AsyncRequestsParser implements Distribution {
|
||||||
|
|
||||||
private static final Logger logger = LoggerManager.getLogger("ArchParser");
|
private static final Logger logger = LoggerManager.getLogger("ArchDistribution");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will return the String json of the package from the Arch Linux API
|
* Will return the String json of the package from the Arch Linux API
|
||||||
* @param packageName the package name to get the json from
|
* @param packageName the package name to get the json from
|
||||||
* @return json of the package
|
* @return json of the package
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<HttpResponse<String>> getPackageFromAPI(String packageName) {
|
|
||||||
// create a new http client
|
@Override
|
||||||
HttpClient client = HttpClient.newHttpClient();
|
public CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String packageName) {
|
||||||
// and create its url
|
// create a new http client
|
||||||
HttpRequest request = HttpRequest.newBuilder(URI.create("https://archlinux.org/packages/search/json/?name="+packageName)).build();
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
// send its url and return the string given
|
// and create its url
|
||||||
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
|
HttpRequest request = HttpRequest.newBuilder(URI.create("https://archlinux.org/packages/search/json/?name="+packageName)).build();
|
||||||
}
|
|
||||||
|
CompletableFuture<Pair<Package, Set<String>>> futureResult = new CompletableFuture<>();
|
||||||
|
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result ->{
|
||||||
|
|
||||||
|
JSONObject json = new JSONObject(result.body());
|
||||||
|
|
||||||
|
JSONArray resultsArrayJson = json.getJSONArray("results");
|
||||||
|
if(resultsArrayJson.length()==0){
|
||||||
|
// unknown package, probably an abstract dependency
|
||||||
|
futureResult.complete(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JSONObject resultJson = resultsArrayJson.getJSONObject(0);
|
||||||
|
|
||||||
|
// get infos
|
||||||
|
|
||||||
|
Set<String> dependenciesNames = new HashSet<>();
|
||||||
|
for(Object dependency : resultJson.getJSONArray("depends")){
|
||||||
|
dependenciesNames.add((String)dependency);
|
||||||
|
}
|
||||||
|
futureResult.complete(new Pair<>(
|
||||||
|
new Package(
|
||||||
|
resultJson.getString("pkgname"),
|
||||||
|
resultJson.getString("pkgver"),
|
||||||
|
resultJson.getString("repo"),
|
||||||
|
resultJson.getString("pkgdesc")
|
||||||
|
),
|
||||||
|
dependenciesNames
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
return futureResult;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,85 +106,4 @@ public class ArchDistribution implements Distribution {
|
|||||||
return searchedPackagesList;
|
return searchedPackagesList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will generate a dependencies tree of depth 'depth' given the package name
|
|
||||||
* @param packageName the package name to search
|
|
||||||
* @param depth depth to search dependencies
|
|
||||||
* @return new Package
|
|
||||||
*/
|
|
||||||
public CompletableFuture<Package> getPackageTree(String packageName, int depth) {
|
|
||||||
|
|
||||||
// parse the json
|
|
||||||
var futurePackage = new CompletableFuture<Package>();
|
|
||||||
|
|
||||||
logger.fine("Querying package %s from API... (depth=%s)".formatted(packageName, depth));
|
|
||||||
|
|
||||||
CompletableFuture<HttpResponse<String>> futureRequest;
|
|
||||||
try{
|
|
||||||
futureRequest = getPackageFromAPI(packageName);
|
|
||||||
}catch(IllegalArgumentException e){
|
|
||||||
logger.warning("Caught exception for package %s :\n%s".formatted(packageName, e));
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
futureRequest.thenAccept(result->{
|
|
||||||
List<Package> deps = new ArrayList<>();
|
|
||||||
String name, version, repo, description;
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject(result.body());
|
|
||||||
|
|
||||||
JSONArray resultsArrayJson = json.getJSONArray("results");
|
|
||||||
if(resultsArrayJson.length()==0){
|
|
||||||
// unknown package, probably an abstract dependency
|
|
||||||
logger.fine("Completing callback INVALID for package %s (depth=%s)".formatted(packageName, depth));
|
|
||||||
futurePackage.complete(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
JSONObject resultJson = resultsArrayJson.getJSONObject(0);
|
|
||||||
|
|
||||||
// get infos except dependencies
|
|
||||||
name = resultJson.getString("pkgname");
|
|
||||||
version = resultJson.getString("pkgver");
|
|
||||||
repo = resultJson.getString("repo");
|
|
||||||
description = resultJson.getString("pkgdesc");
|
|
||||||
|
|
||||||
// if we're at the maximum depth, return the package without its dependencies
|
|
||||||
if(depth==0) {
|
|
||||||
logger.fine("Completing callback NODEP for package %s (depth=%s)".formatted(packageName, depth));
|
|
||||||
futurePackage.complete(new Package(name, version, repo, description, Collections.emptyList()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// iterate for every package in the list
|
|
||||||
List<CompletableFuture<Package>> futureDeps = new ArrayList<>();
|
|
||||||
for (Object depPackageNameObj : resultJson.getJSONArray("depends")) {
|
|
||||||
// convert object into String
|
|
||||||
String depPackageName = (String) depPackageNameObj;
|
|
||||||
// add package into Package List
|
|
||||||
futureDeps.add(getPackageTree(depPackageName, depth - 1));
|
|
||||||
}
|
|
||||||
for(CompletableFuture<Package> future : futureDeps){
|
|
||||||
Package dep;
|
|
||||||
try {
|
|
||||||
dep = future.get();
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
if(dep!=null){
|
|
||||||
deps.add(dep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this doesn't seem clean
|
|
||||||
logger.fine("Completing callback DEPS for package %s (depth=%s)".formatted(packageName, depth));
|
|
||||||
futurePackage.complete(new Package(name, version, repo, description, deps));
|
|
||||||
}).exceptionally((e2->{
|
|
||||||
logger.warning("Error while fetching package %s (depth=%s) from the API : \n%s".formatted(packageName, depth, e2));
|
|
||||||
e2.printStackTrace();
|
|
||||||
futurePackage.complete(null);
|
|
||||||
return null;
|
|
||||||
}));
|
|
||||||
|
|
||||||
return futurePackage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,11 @@ package fr.packageviewer.distribution;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.net.http.*;
|
import java.net.http.*;
|
||||||
|
|
||||||
|
import fr.packageviewer.Pair;
|
||||||
|
import fr.packageviewer.parser.AsyncRequestsParser;
|
||||||
import org.json.*;
|
import org.json.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import fr.packageviewer.pack.Package;
|
import fr.packageviewer.pack.Package;
|
||||||
@ -17,25 +15,54 @@ import fr.packageviewer.LoggerManager;
|
|||||||
import fr.packageviewer.pack.Package;
|
import fr.packageviewer.pack.Package;
|
||||||
import fr.packageviewer.pack.SearchedPackage;
|
import fr.packageviewer.pack.SearchedPackage;
|
||||||
|
|
||||||
public class FedoraDistribution implements Distribution {
|
public class FedoraDistribution extends AsyncRequestsParser implements Distribution {
|
||||||
|
|
||||||
private String getPackageFromAPI(String packageName) {
|
protected CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String packageName) {
|
||||||
// create a new http client
|
// create a new http client
|
||||||
HttpClient client = HttpClient.newHttpClient();
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
// and create its url
|
// and create its url
|
||||||
String url = "https://mdapi.fedoraproject.org/rawhide/pkg/"+packageName+"";
|
String url = "https://mdapi.fedoraproject.org/rawhide/pkg/"+packageName+"";
|
||||||
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
|
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
|
||||||
// send its url and return the string given
|
|
||||||
try {
|
|
||||||
String response = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
|
|
||||||
if(response.contains("404: Not Found")) return "";
|
|
||||||
return response;
|
|
||||||
} catch (IOException|InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
|
|
||||||
}
|
CompletableFuture<Pair<Package, Set<String>>> futureResult = new CompletableFuture<>();
|
||||||
|
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result->{
|
||||||
|
|
||||||
|
String body = result.body();
|
||||||
|
|
||||||
|
if(body.contains("404: Not Found")) {
|
||||||
|
futureResult.complete(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject json = new JSONObject(result.body());
|
||||||
|
|
||||||
|
// get infos
|
||||||
|
Set<String> dependenciesNames = new HashSet<>();
|
||||||
|
|
||||||
|
for (Object depPackageObj : json.getJSONArray("requires")) {
|
||||||
|
// convert object into String
|
||||||
|
JSONObject depPackageJson = (JSONObject) depPackageObj;
|
||||||
|
// add package into Package List
|
||||||
|
String depName = depPackageJson.getString("name");
|
||||||
|
if (depName.contains(".so"))
|
||||||
|
continue;
|
||||||
|
if (depName.contains("/"))
|
||||||
|
continue;
|
||||||
|
dependenciesNames.add(depName);
|
||||||
|
}
|
||||||
|
|
||||||
|
futureResult.complete(new Pair<>(
|
||||||
|
new Package(
|
||||||
|
json.getString("basename"),
|
||||||
|
json.getString("version"),
|
||||||
|
json.getString("repo"),
|
||||||
|
json.getString("description")
|
||||||
|
),
|
||||||
|
dependenciesNames
|
||||||
|
));
|
||||||
|
});
|
||||||
// if there's an error, return an empty string
|
// if there's an error, return an empty string
|
||||||
return "";
|
return futureResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -73,46 +100,4 @@ public class FedoraDistribution implements Distribution {
|
|||||||
}
|
}
|
||||||
return searchedPackagesList;
|
return searchedPackagesList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Package getPackageTreeInternal(String packageName, int depth) {
|
|
||||||
String name, version, repo, description;
|
|
||||||
List<Package> deps = new ArrayList<>();
|
|
||||||
|
|
||||||
// parse the json
|
|
||||||
String response = getPackageFromAPI(packageName);
|
|
||||||
if (response == "") {
|
|
||||||
return new Package(packageName + "(not found)", "N/A", "N/A", "N/A", Collections.emptyList());
|
|
||||||
}
|
|
||||||
JSONObject json = new JSONObject(response);
|
|
||||||
// get infos except dependencies
|
|
||||||
name = json.getString("basename");
|
|
||||||
version = json.getString("version");
|
|
||||||
repo = "rpms/" + packageName;
|
|
||||||
description = json.getString("description");
|
|
||||||
|
|
||||||
// if we're at the maximum depth, return the package without its dependencies
|
|
||||||
if (depth == 0) {
|
|
||||||
return new Package(name, version, repo, description, Collections.emptyList());
|
|
||||||
} else {
|
|
||||||
// iterate for every package in the list
|
|
||||||
for (Object depPackageNameObj : json.getJSONArray("requires")) {
|
|
||||||
// convert object into String
|
|
||||||
JSONObject depPackageJSONObj = (JSONObject) depPackageNameObj;
|
|
||||||
// add package into Package List
|
|
||||||
String depName = depPackageJSONObj.getString("name");
|
|
||||||
if (depName.contains(".so"))
|
|
||||||
continue;
|
|
||||||
if (depName.contains("/"))
|
|
||||||
continue;
|
|
||||||
deps.add(getPackageTreeInternal(depName, depth - 1));
|
|
||||||
}
|
|
||||||
return new Package(name, version, repo, description, deps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<Package> getPackageTree(String packageName, int depth){
|
|
||||||
return CompletableFuture.supplyAsync(()->{
|
|
||||||
return getPackageTreeInternal(packageName, depth);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package fr.packageviewer.pack;
|
package fr.packageviewer.pack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Package extends SearchedPackage {
|
public class Package extends SearchedPackage {
|
||||||
@ -9,6 +11,13 @@ public class Package extends SearchedPackage {
|
|||||||
return deps;
|
return deps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addDep(Package pack) {
|
||||||
|
deps.add(pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Package(String name, String version, String repo, String description) {
|
||||||
|
this(name, version, repo, description, new ArrayList<>());
|
||||||
|
}
|
||||||
public Package(String name, String version, String repo, String description, List<Package> deps) {
|
public Package(String name, String version, String repo, String description, List<Package> deps) {
|
||||||
super(name, version, repo, description);
|
super(name, version, repo, description);
|
||||||
this.deps = deps;
|
this.deps = deps;
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package fr.packageviewer.parser;
|
||||||
|
|
||||||
|
import fr.packageviewer.LoggerManager;
|
||||||
|
import fr.packageviewer.Pair;
|
||||||
|
import fr.packageviewer.pack.Package;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public abstract class AsyncRequestsParser {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerManager.getLogger("AsyncRequestsParser");
|
||||||
|
|
||||||
|
protected abstract CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String name);
|
||||||
|
|
||||||
|
public CompletableFuture<Package> getPackageTree(String packageName, int depth) {
|
||||||
|
// parse the json
|
||||||
|
var futurePackage = new CompletableFuture<Package>();
|
||||||
|
|
||||||
|
logger.fine("Querying package %s from API... (depth=%s)".formatted(packageName, depth));
|
||||||
|
|
||||||
|
CompletableFuture<Pair<Package, Set<String>>> futureRequest;
|
||||||
|
try {
|
||||||
|
futureRequest = getPackageFromAPI(packageName);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.warning("Caught exception for package %s :\n%s".formatted(packageName, e));
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
futureRequest.thenAccept(result -> {
|
||||||
|
if(result==null){
|
||||||
|
logger.fine("Completing callback INVALID for package %s (depth=%s)".formatted(packageName, depth));
|
||||||
|
futurePackage.complete(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Package pack = result.getFirst();
|
||||||
|
Set<String> dependenciesNames = result.getSecond();
|
||||||
|
|
||||||
|
|
||||||
|
// if we're at the maximum depth, return the package without its dependencies
|
||||||
|
if (depth == 0) {
|
||||||
|
logger.fine("Completing callback NODEP for package %s (depth=%s)".formatted(packageName, depth));
|
||||||
|
futurePackage.complete(pack);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate for every package in the list
|
||||||
|
List<CompletableFuture<Package>> futureDeps = new ArrayList<>();
|
||||||
|
for (String depPackageName : dependenciesNames) {
|
||||||
|
// convert object into String
|
||||||
|
// add package into Package List
|
||||||
|
futureDeps.add(getPackageTree(depPackageName, depth - 1));
|
||||||
|
}
|
||||||
|
for (CompletableFuture<Package> future : futureDeps) {
|
||||||
|
Package dep;
|
||||||
|
try {
|
||||||
|
dep = future.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (dep != null) {
|
||||||
|
pack.addDep(dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this doesn't seem clean
|
||||||
|
logger.fine("Completing callback DEPS for package %s (depth=%s)".formatted(packageName, depth));
|
||||||
|
futurePackage.complete(pack);
|
||||||
|
}).exceptionally((e2 -> {
|
||||||
|
logger.warning("Error while fetching package %s (depth=%s) from the API : \n%s".formatted(packageName, depth, e2));
|
||||||
|
e2.printStackTrace();
|
||||||
|
futurePackage.complete(null);
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return futurePackage;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user