Merge pull request #32 from ThomasRubini/documentation

This commit is contained in:
Thomas Rubini 2022-12-15 13:26:29 +01:00 committed by GitHub
commit ff4a66571a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 355 additions and 82 deletions

View File

@ -2,15 +2,37 @@ package fr.packageviewer;
import java.util.logging.*;
/**
* The LoggerManager class allows for basic debug output management using
* Java's default logging class.
*
* @author R.Thomas
* @version 1.0
*/
public class LoggerManager {
/**
* Default log level, should be INFO
*/
private static final Level DEFAULT_LOG_LEVEL = Level.INFO;
public static Logger getLogger(String name){
/**
* Static factory for the Logger class
*
* @param name String, of the logger to create
* @return Logger, a new logger
*/
public static Logger getLogger(String name) {
return getLogger(name, DEFAULT_LOG_LEVEL);
}
public static Logger getLogger(String name, Level level){
/**
* Main static factory for the Logger class
*
* @param name String, name of the logger to create
* @param level Level, the level of severity of the logger
* @return Logger, a new logger
*/
public static Logger getLogger(String name, Level level) {
Logger logger = Logger.getLogger(name);
logger.setLevel(level);

View File

@ -1,34 +1,74 @@
package fr.packageviewer;
public class Pair<K,V> {
/**
* The Pair class stores two objects of distinct type
*
* @author R.Thomas
* @version 1.0
*/
public class Pair<K, V> {
private K first;
private V second;
/**
* Empty Constructor for the Pair class
*/
public Pair() {
}
/**
* Main Constructor for the Pair class
*
* @param first, the first object to be stored
* @param second the second object to be stored
*/
public Pair(K first, V second) {
this.first = first;
this.second = second;
}
/**
* Getter for the attribute first
*
* @return the object stored in the attribute first
*/
public K getFirst() {
return first;
}
/**
* Setter for the attribute first
*
* @param first Store the given object in the attribute first
*/
public void setFirst(K first) {
this.first = first;
}
/**
* Getter for the attribute second
*
* @return the object stored in the attribute second
*/
public V getSecond() {
return second;
}
/**
* Setter for the attribute second
*
* @param second Store the given object in the attribute second
*/
public void setSecond(V second) {
this.second = second;
}
/**
* Returns a string reprensentation of the pair
*
* @return String, string reprensentation of the pair
*/
@Override
public String toString() {
return "Pair{key=%s,value=%s}".formatted(first, second);

View File

@ -15,85 +15,108 @@ import fr.packageviewer.pack.SearchedPackage;
import fr.packageviewer.parser.AsyncRequestsParser;
import org.json.*;
/**
* This class handles package requests for Arch linux. All return objects in
* this class are wrapped by a CompletableFuture to ensure async workload.
*
* @author C.Marla, R.Thomas
* @version 1.0
*/
public class ArchDistribution extends AsyncRequestsParser implements Distribution {
/**
* Logger object used to split debug output and the application output
*/
private static final Logger logger = LoggerManager.getLogger("ArchDistribution");
private static String trimAfterCharacters(String str, String trimAfterCharacters){
for(char c : trimAfterCharacters.toCharArray()){
/**
* This method remove all characters in the first string passed as
* parametter after one of the character in the second string if found
* in the first string
*
* @param str String, the string to trim
* @param trimAfterCharacters String, the character that delimits our string
* @return
*/
private static String trimAfterCharacters(String str, String trimAfterCharacters) {
for (char c : trimAfterCharacters.toCharArray()) {
int index = str.indexOf(c);
if(index>0)str = str.substring(index);
if (index > 0)
str = str.substring(index);
}
return str;
}
/**
* Will return the String json of the package from the Arch Linux API
* @param packageName the package name to get the json from
* @return json of the package
*/
/**
* This function return a package from arch package api in the form of a Pair
* Composed of a Package object, and a set of string containing the names of
* the dependecies of the package.
*
* @param packageName String, The package's exact name
* @return Pair of Package and Set of String
*/
@Override
public CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String packageName) {
// create a new http client
HttpClient client = HttpClient.newHttpClient();
// and create its url
HttpRequest request = HttpRequest
.newBuilder(URI.create("https://archlinux.org/packages/search/json/?name=" + packageName)).build();
@Override
public CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String packageName) {
// create a new http client
HttpClient client = HttpClient.newHttpClient();
// and create its url
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 -> {
CompletableFuture<Pair<Package, Set<String>>> futureResult = new CompletableFuture<>();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result ->{
JSONObject json = new JSONObject(result.body());
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);
JSONArray resultsArrayJson = json.getJSONArray("results");
if(resultsArrayJson.length()==0){
// unknown package, probably an abstract dependency
// get infos
Set<String> dependenciesNames = new HashSet<>();
for (Object dependency : resultJson.getJSONArray("depends")) {
dependenciesNames.add(trimAfterCharacters((String) dependency, "<>="));
}
futureResult.complete(new Pair<>(
new Package(
resultJson.getString("pkgname"),
resultJson.getString("pkgver"),
resultJson.getString("repo"),
resultJson.getString("pkgdesc"),
"arch"),
dependenciesNames));
}).exceptionally(error -> {
error.printStackTrace();
logger.warning("Error while fetching package %s from the API : \n%s".formatted(packageName, error));
futureResult.complete(null);
return;
}
JSONObject resultJson = resultsArrayJson.getJSONObject(0);
return null;
});
// get infos
return futureResult;
Set<String> dependenciesNames = new HashSet<>();
for(Object dependency : resultJson.getJSONArray("depends")){
dependenciesNames.add(trimAfterCharacters((String)dependency, "<>="));
}
futureResult.complete(new Pair<>(
new Package(
resultJson.getString("pkgname"),
resultJson.getString("pkgver"),
resultJson.getString("repo"),
resultJson.getString("pkgdesc"),
"arch"
),
dependenciesNames
));
}).exceptionally(error ->{
error.printStackTrace();
logger.warning("Error while fetching package %s from the API : \n%s".formatted(packageName, error));
futureResult.complete(null);
return null;
});
}
return futureResult;
}
/**
* Search for a package and return a list of packages
* @param packageName the package name to search
* @return
*/
public CompletableFuture<List<SearchedPackage>> searchPackage(String packageName){
/**
* Search for a package matching a pattern and return a list of packages and
* return a list of string matching this pattern.
*
* @param packageName String, the pattern to search in the repositories
* @return List of SearchedPackage objects
*/
public CompletableFuture<List<SearchedPackage>> searchPackage(String packageName) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://archlinux.org/packages/search/json/?q="+packageName))
.uri(URI.create("https://archlinux.org/packages/search/json/?q=" + packageName))
.build();
CompletableFuture<List<SearchedPackage>> futureSearchedPackages = new CompletableFuture<>();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result->{
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result -> {
List<SearchedPackage> searchedPackagesList = new ArrayList<>();
JSONObject json = new JSONObject(result.body());
@ -104,22 +127,19 @@ public CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String pa
JSONObject searchResultJson = (JSONObject) searchResultObj;
// add package into to list
searchedPackagesList.add(new SearchedPackage(
searchResultJson.getString("pkgname"),
searchResultJson.getString("pkgver"),
searchResultJson.getString("repo"),
searchResultJson.getString("pkgdesc"),
"arch"
));
searchResultJson.getString("pkgname"),
searchResultJson.getString("pkgver"),
searchResultJson.getString("repo"),
searchResultJson.getString("pkgdesc"),
"arch"));
}
futureSearchedPackages.complete(searchedPackagesList);
}).exceptionally(error->{
futureSearchedPackages.complete(searchedPackagesList);
}).exceptionally(error -> {
error.printStackTrace();
futureSearchedPackages.complete(Collections.emptyList());
return null;
});
return futureSearchedPackages;
}

View File

@ -7,7 +7,32 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
/**
* This interface specifies the methods needed by a distribtion to be parsable.
*
* @author R.Thomas
* @version 1.0
*/
public interface Distribution {
/**
* Search for a package matching a pattern and return a list of packages and
* return a list of string matching this pattern.
*
* @param packageName String, the pattern to search in the repositories
* @return List of SearchedPackage objects
*/
Future<List<SearchedPackage>> searchPackage(String packageName);
/**
* This function returns a fully completed package containing all
* information about the package identified by it's exact name passed as
* parametter, the package contains in its dependency list fully formed
* packages that also contains its dependencies, the dependency depth is
* specified by the parametter with the same name.
*
* @param packageName String, The package's exact name
* @param depth int, the depth of the dependency tree
* @return Package, the fully completed package
*/
Future<Package> getPackageTree(String packageName, int depth);
}

View File

@ -14,23 +14,41 @@ import fr.packageviewer.pack.Package;
import fr.packageviewer.pack.SearchedPackage;
import fr.packageviewer.LoggerManager;
/**
* This class handles package requests for Fedora. All return objects in
* this class are wrapped by a CompletableFuture to ensure async workload.
*
* @author S.Djalim, R.Thomas
* @version 1.0
*/
public class FedoraDistribution extends AsyncRequestsParser implements Distribution {
/**
* Logger object used to split debug output and the application output
*/
private static final Logger logger = LoggerManager.getLogger("FedoraDistribution");
/**
* This function return a package from Fedora metadata api in the form of a
* Pair Composed of a Package object, and a set of string containing the
* names of the dependecies of the package.
*
* @param packageName String, The package's exact name
* @return Pair of Package and Set of String
*/
protected CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String packageName) {
// create a new http client
HttpClient client = HttpClient.newHttpClient();
// 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();
CompletableFuture<Pair<Package, Set<String>>> futureResult = new CompletableFuture<>();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result->{
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result -> {
String body = result.body();
if(body.contains("404: Not Found")) {
if (body.contains("404: Not Found")) {
futureResult.complete(null);
return;
}
@ -71,7 +89,14 @@ public class FedoraDistribution extends AsyncRequestsParser implements Distribut
// if there's an error, return an empty string
return futureResult;
}
/**
* Search for a package matching a pattern and return a list of packages and
* return a list of string matching this pattern.
*
* @param packageName String, the pattern to search in the repositories
* @return List of SearchedPackage objects
*/
@Override
public CompletableFuture<List<SearchedPackage>> searchPackage(String packageName) {
@ -84,7 +109,7 @@ public class FedoraDistribution extends AsyncRequestsParser implements Distribut
CompletableFuture<List<SearchedPackage>> futureSearchedPackages = new CompletableFuture<>();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result->{
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(result -> {
JSONObject json = new JSONObject(result.body());
List<SearchedPackage> searchedPackagesList = new ArrayList<>();
@ -112,7 +137,7 @@ public class FedoraDistribution extends AsyncRequestsParser implements Distribut
}
futureSearchedPackages.complete(searchedPackagesList);
}).exceptionally(error->{
}).exceptionally(error -> {
error.printStackTrace();
futureSearchedPackages.complete(Collections.emptyList());
return null;

View File

@ -4,25 +4,73 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The Package class stores all metadata needed for a fully completed package.
*
* @author C.Marla, R.Thomas, S.Djalim
* @version 1.0
*/
public class Package extends SearchedPackage {
/**
* List of package storing all of the dependencies of the package
*/
private final List<Package> deps;
/**
* Getter for the deps attribute
*
* @return List, List of package storing all of the dependencies of the package
*/
public List<Package> getDeps() {
return deps;
}
/**
* This method adds to the dependency list the package passed as parametter.
*
* @param pack Package, the package to add as dependency
*/
public void addDep(Package pack) {
deps.add(pack);
}
/**
* Second constructor for the Package class, allows to create a package
* without supplying a list of dependencies.
*
* @param name String, name of the package
* @param version String, version of the package
* @param repo String, repository where the package is located
* @param description String, description of the package
* @param distribution String, the distribution where this specific package
* belongs
*/
public Package(String name, String version, String repo, String description, String distribution) {
this(name, version, repo, description, distribution, new ArrayList<>());
}
public Package(String name, String version, String repo, String description, String distribution, List<Package> deps) {
/**
* Main constructor for the Package class
*
* @param name String, name of the package
* @param version String, version of the package
* @param repo String, repository where the package is located
* @param description String, description of the package
* @param distribution String, the distribution where this specific package
* belongs
* @param deps List of Package, dependencies of the package
*/
public Package(String name, String version, String repo, String description, String distribution,
List<Package> deps) {
super(name, version, repo, description, distribution);
this.deps = deps;
}
/**
* Returns a string reprensentation of the package
*
* @return String, string reprensentation of the package
*/
@Override
public String toString() {
return "Package{%s,deps=%s}".formatted(super.toString(), deps);

View File

@ -1,32 +1,89 @@
package fr.packageviewer.pack;
/**
* The SearchedPackage class stores metadata found when searching for a
* package.
*
* @author C.Marla, R.Thomas, S.Djalim
* @version 1.0
*/
public class SearchedPackage {
/**
* Name of the package
*/
private final String name;
/**
* Version of the package
*/
private final String version;
/**
* Repository where the package is located
*/
private final String repo;
/**
* Description of the package
*/
private final String description;
/**
* Distribution where this specific package belongs
*/
private final String distribution;
/**
* Getter for the name attribute
*
* @return String, the name of the package
*/
public String getName() {
return name;
}
/**
* Getter for the version attribute
*
* @return String, the version of the package
*/
public String getVersion() {
return version;
}
/**
* Getter for the repo attribute
*
* @return String, repository where the package is located
*/
public String getRepo() {
return repo;
}
/**
* Getter for the description attribute
*
* @return String, Description of the package
*/
public String getDescription() {
return description;
}
/**
* Getter for the distribution attribute
*
* @return String, distribution where this specific package belongs
*/
public String getDistribution() {
return distribution;
}
/**
* Constructor for the SearchedPackage class
*
* @param name String, name of the package
* @param version String, version of the package
* @param repo String, repository where the package is located
* @param description String, description of the package
* @param distribution String, the distribution where this specific package
* belongs
*/
public SearchedPackage(String name, String version, String repo, String description, String distribution) {
this.name = name;
this.version = version;
@ -35,8 +92,14 @@ public class SearchedPackage {
this.distribution = distribution;
}
/**
* Returns a string reprensentation of the package
*
* @return String, string reprensentation of the package
*/
@Override
public String toString() {
return "SearchedPackage{name=%s,version=%s,repo=%s,description=%s,distribution=%s}".formatted(name, version, repo, description, distribution);
return "SearchedPackage{name=%s,version=%s,repo=%s,description=%s,distribution=%s}".formatted(name, version,
repo, description, distribution);
}
}

View File

@ -11,12 +11,42 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
/**
* This abstract class defines the method that a distribution will use
* in order to get a package and fill its dependency list. It does all that
* in an asyncron manner
*
* @author R.Thomas
* @version 1.0
*
*/
public abstract class AsyncRequestsParser {
/**
* Logger object used to split debug output and the application output
*/
private static final Logger logger = LoggerManager.getLogger("AsyncRequestsParser");
/**
* This function returns a package from the distribution's api in the form
* of a Pair Composed of a Package object and a set of string containing
* the names of the dependecies of the package.
*
* @param packageName String, The package's exact name
* @return Pair of Package and Set of String
*/
protected abstract CompletableFuture<Pair<Package, Set<String>>> getPackageFromAPI(String name);
/**
* This function returns a fully completed package containing all
* information about the package identified by it's exact name passed as
* parametter, the package contains in its dependency list fully formed
* packages that also contains its dependencies, the dependency depth is
* specified by the parametter with the same name.
*
* @param packageName String, The package's exact name
* @param depth int, the depth of the dependency tree
* @return Package, the fully completed package
*/
public CompletableFuture<Package> getPackageTree(String packageName, int depth) {
// parse the json
var futurePackage = new CompletableFuture<Package>();
@ -31,7 +61,7 @@ public abstract class AsyncRequestsParser {
return CompletableFuture.completedFuture(null);
}
futureRequest.thenAccept(result -> {
if(result==null){
if (result == null) {
logger.fine("Completing callback INVALID for package %s (depth=%s)".formatted(packageName, depth));
futurePackage.complete(null);
return;
@ -39,7 +69,6 @@ public abstract class AsyncRequestsParser {
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));
@ -71,7 +100,8 @@ public abstract class AsyncRequestsParser {
futurePackage.complete(pack);
}).exceptionally(error -> {
error.printStackTrace();
logger.warning("Error while manipulating package %s (depth=%s) : \n%s".formatted(packageName, depth, error));
logger.warning(
"Error while manipulating package %s (depth=%s) : \n%s".formatted(packageName, depth, error));
futurePackage.complete(null);
return null;
});