Skip to content

Commit

Permalink
refactor(rest): enhance logging and error handling in FossologyRestCl…
Browse files Browse the repository at this point in the history
…ient

- Added detailed debug logs before HTTP requests (upload, scan, report generation).
- Included relevant context like filename, uploadId, jobId, reportId, and shaValue.
- Improved error handling with better exception messages and stack traces.
- Fixed typos in log messages and standardized formats for consistency.
- Improved null checks to prevent potential `NullPointerException`.
- Enhanced logging in error cases with specific messages and stack traces.
- Ensured proper handling of failed API responses with meaningful logs.
  • Loading branch information
Mohamed-hanfy committed Mar 11, 2025
1 parent cb52c1a commit 8f88e8a
Showing 1 changed file with 161 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,12 @@ public int uploadFile(String filename, InputStream fileStream, String uploadDesc

FossologyResponse response = null;
try {
log.debug("Attempting to upload file {} to Fossology at {}", filename, baseUrl + "uploads");
response = restTemplate.postForObject(baseUrl + "uploads", requestEntity, FossologyResponse.class);
log.debug(filename + "uploaded: " + response.getCode() + " - " + response.getMessage() + " - " + response.getType());
log.debug(filename + " uploaded: " + response.getCode() + " - " + response.getMessage() + " - " + response.getType());
} catch (RestClientException e) {
log.error("Error while trying to upload file {}.", e, filename);
log.error("Error while uploading file '{}' to Fossology. HTTP request failed: {}", filename, e.getMessage());
log.debug("Detailed upload error:", e);
return -1;
}

Expand All @@ -180,8 +182,11 @@ public int uploadFile(String filename, InputStream fileStream, String uploadDesc
try {
return Integer.valueOf(response.getMessage());
} catch (NumberFormatException e) {
log.error("Fossology REST returned non integer uploadId: {}", e, response.getMessage());
log.error("Fossology REST returned non-integer uploadId: '{}'. Error: {}", response.getMessage(), e.getMessage());
log.debug("Full number format exception:", e);
}
} else {
log.error("Fossology upload failed with status code {} and message: {}", response.getCode(), response.getMessage());
}

return -1;
Expand All @@ -208,7 +213,7 @@ public int startScanning(int uploadId) {
}

if (uploadId < 0) {
log.error("Invalid arguments, uploadId must not be less thann 0!");
log.error("Invalid arguments, uploadId must not be less than 0!");
return -1;
}

Expand Down Expand Up @@ -247,14 +252,22 @@ public int startScanning(int uploadId) {

FossologyResponse response;
try {
log.debug("Starting scan job for uploadId {} at {}", uploadId, baseUrl + "jobs");
response = restTemplate.postForObject(baseUrl + "jobs", new HttpEntity<>(requestBody, headers),
FossologyResponse.class);
} catch (RestClientException e) {
log.error("Error while trying to start scanning of upload {}.", e, uploadId);
log.error("Error while starting scanning job for upload {} in Fossology: {}", uploadId, e.getMessage());
log.debug("Detailed scanning error:", e);
return -1;
}

return HttpStatus.valueOf(response.getCode()).is2xxSuccessful() ? Integer.valueOf(response.getMessage()) : -1;
if (HttpStatus.valueOf(response.getCode()).is2xxSuccessful()) {
log.debug("Successfully started scan job for uploadId {}, received jobId {}", uploadId, response.getMessage());
return Integer.valueOf(response.getMessage());
} else {
log.error("Failed to start scan job for uploadId {}. Status: {}, Message: {}", uploadId, response.getCode(), response.getMessage());
return -1;
}
}

/**
Expand All @@ -275,7 +288,7 @@ public int startScanning(int uploadId) {
}

if (jobId < 0) {
log.error("Invalid arguments, jobId must not be less thann 0!");
log.error("Invalid arguments, jobId must not be less than 0!");
return responseMap;
}

Expand All @@ -284,22 +297,38 @@ public int startScanning(int uploadId) {

ResponseEntity<JsonNode> response;
try {
log.debug("Checking scan status for jobId {} at {}", jobId, baseUrl + "jobs/" + jobId);
response = restTemplate.exchange(baseUrl + "jobs/" + jobId, HttpMethod.GET, new HttpEntity<>(headers),
JsonNode.class);
} catch (RestClientException e) {
log.error("Error while trying to query status of scanning process with job id {}.", e, jobId);
log.error("Error while querying scan status for job id {}: {}", jobId, e.getMessage());
log.debug("Detailed scan status error:", e);
return responseMap;
}

String status = response.getBody().findValuesAsText("status").get(0);
int eta = response.getBody().get("eta").intValue();
responseMap.put("eta", eta+"");
responseMap.put("status", status);
JsonNode body = response.getBody();
if (body == null) {
log.error("Received empty response body when checking scan status for job id {}", jobId);
return responseMap;
}

try {
String status = body.findValuesAsText("status").get(0);
int eta = body.get("eta").intValue();
responseMap.put("eta", eta+"");
responseMap.put("status", status);
log.debug("Scan status for jobId {}: Status={}, ETA={}", jobId, status, eta);
} catch (Exception e) {
log.error("Error parsing scan status response for job id {}: {}", jobId, e.getMessage());
log.debug("Response body was: {}", body);
log.debug("Detailed parse error:", e);
}

return responseMap;
}

/**
* Triggers a report generatoin of a former upload whose uploadId must be given.
* Triggers a report generation of a former upload whose uploadId must be given.
* The report will have the format
* {@link FossologyRestClient#PARAMETER_VALUE_REPORT_FORMAT_SPDX2}. Be aware
* that the report can be generated even though a scan of the upload did not
Expand All @@ -319,7 +348,7 @@ public int startReport(int uploadId) {
}

if (uploadId < 0) {
log.error("Invalid arguments, uploadId must not be less thann 0!");
log.error("Invalid arguments, uploadId must not be less than 0!");
return -1;
}

Expand All @@ -330,17 +359,34 @@ public int startReport(int uploadId) {

FossologyResponse response;
try {
log.debug("Starting report generation for uploadId {} at {}", uploadId, baseUrl + "report");
ResponseEntity<FossologyResponse> responseEntity = restTemplate.exchange(baseUrl + "report", HttpMethod.GET,
new HttpEntity<>(headers), FossologyResponse.class);
response = responseEntity.getBody();
if (response == null) {
log.error("Received null response body when starting report generation for uploadId {}", uploadId);
return -1;
}
} catch (RestClientException e) {
log.error("Error while trying to start report generation of upload {}.", e, uploadId);
log.error("Error while starting report generation for upload {}: {}", uploadId, e.getMessage());
log.debug("Detailed report generation error:", e);
return -1;
}

return HttpStatus.valueOf(response.getCode()).is2xxSuccessful()
? Integer.valueOf(response.getMessage().substring(response.getMessage().lastIndexOf("/") + 1))
: -1;
if (HttpStatus.valueOf(response.getCode()).is2xxSuccessful()) {
String reportId = response.getMessage().substring(response.getMessage().lastIndexOf("/") + 1);
log.debug("Successfully started report generation for uploadId {}, received reportId {}", uploadId, reportId);
try {
return Integer.valueOf(reportId);
} catch (NumberFormatException e) {
log.error("Failed to parse reportId '{}' as integer: {}", reportId, e.getMessage());
return -1;
}
} else {
log.error("Failed to start report generation for uploadId {}. Status: {}, Message: {}",
uploadId, response.getCode(), response.getMessage());
return -1;
}
}

/**
Expand All @@ -365,24 +411,46 @@ public InputStream getReport(int reportId) {
}

if (reportId < 0) {
log.error("Invalid arguments, reportId must not be less thann 0!");
log.error("Invalid arguments, reportId must not be less than 0!");
return null;
}

HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);

try {
log.debug("Downloading report for reportId {} from {}", reportId, baseUrl + "report/" + reportId);
ResponseEntity<Resource> responseEntity = restTemplate.exchange(baseUrl + "report/" + reportId,
HttpMethod.GET, new HttpEntity<>(headers), Resource.class);

return responseEntity.getBody().getInputStream();
} catch (RestClientException | IOException e) {
if (responseEntity.getStatusCode().is2xxSuccessful()) {
Resource reportResource = responseEntity.getBody();
if (reportResource == null) {
log.error("Received null resource when downloading report for reportId {}", reportId);
return null;
}
log.debug("Successfully downloaded report for reportId {}", reportId);
return reportResource.getInputStream();
} else {
log.error("Failed to download report for reportId {}. HTTP status: {}",
reportId, responseEntity.getStatusCodeValue());
return null;
}
} catch (RestClientException e) {
// we could distinguish further since fossology would send a 503 with
// Retry-After header in seconds if the report isn't ready yet. But since our
// workflow is currently completely pull based there would not be a huge
// advantage to use this and it would just complicate things right now.
log.error("Error while trying to download report with id {}.", e, reportId);
if (e.getMessage().contains("503")) {
log.info("Report for reportId {} is not ready yet (Status 503). Will retry later.", reportId);
} else {
log.error("Error while downloading report for reportId {}: {}", reportId, e.getMessage());
log.debug("Detailed report download error:", e);
}
return null;
} catch (IOException e) {
log.error("I/O error when processing downloaded report for reportId {}: {}", reportId, e.getMessage());
log.debug("Detailed I/O error:", e);
return null;
}
}
Expand All @@ -404,32 +472,51 @@ public Map<String, String> checkUnpackStatus(int uploadId) {
}

if (uploadId < 0) {
log.error("Invalid arguments, uploadId must not be less thann 0!");
log.error("Invalid arguments, uploadId must not be less than 0!");
return responseMap;
}

HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
JsonNode response;
try {
log.debug("Checking unpack status for uploadId {} at {}", uploadId, baseUrl + "jobs?upload=" + uploadId);
JsonNode[] responseArr = restTemplate.exchange(baseUrl + "jobs?upload=" + uploadId, HttpMethod.GET,
new HttpEntity<>(headers), JsonNode[].class).getBody();

if (responseArr == null || responseArr.length == 0) {
log.error("Received empty response when checking unpack status for uploadId {}", uploadId);
return responseMap;
}

String uploadIdStr = "" + uploadId;
List<JsonNode> uploadStatus = Arrays.stream(responseArr)
.filter(node -> uploadIdStr.equals(node.findValuesAsText("uploadId").get(0)))
.filter(node -> {
List<String> uploadIds = node.findValuesAsText("uploadId");
return !uploadIds.isEmpty() && uploadIdStr.equals(uploadIds.get(0));
})
.collect(Collectors.toList());

if (!uploadStatus.isEmpty()) {
response = uploadStatus.get(0);
} else
return responseMap;

String status = response.findValuesAsText("status").get(0);
responseMap.put("status", status);
log.debug("Unpack status for uploadId {}: {}", uploadId, status);
} else {
log.warn("No status information found for uploadId {} in Fossology response", uploadId);
}

return responseMap;
} catch (RestClientException e) {
log.error("Error: {} while trying to query status of scanning process with jobId: {}.", e, uploadId);
log.error("Error while checking unpack status for uploadId {}: {}", uploadId, e.getMessage());
log.debug("Detailed unpack status error:", e);
return responseMap;
} catch (Exception e) {
log.error("Unexpected error while processing unpack status for uploadId {}: {}", uploadId, e.getMessage());
log.debug("Detailed error:", e);
return responseMap;
}

String status = response.findValuesAsText("status").get(0);
responseMap.put("status", status);
return responseMap;
}

public int getFolderId(String uploadId) {
Expand All @@ -445,16 +532,29 @@ public int getFolderId(String uploadId) {
headers.set("Authorization", "Bearer " + token);

try {
log.debug("Getting folder details for uploadId {} from {}", uploadId, baseUrl + "uploads/" + uploadId);
ResponseEntity<JsonNode> response = restTemplate.exchange(baseUrl + "uploads/" + uploadId,
HttpMethod.GET, new HttpEntity<>(headers), JsonNode.class);

JsonNode responseBody = response.getBody();
if (responseBody == null) {
log.error("Received null response body when getting folder details for uploadId {}", uploadId);
return -1;
}

if (!responseBody.has("folderid")) {
log.error("Response for uploadId {} does not contain 'folderid' field", uploadId);
log.debug("Response body was: {}", responseBody);
return -1;
}

JsonNode folderId = responseBody.get("folderid");
return folderId.asInt();
int result = folderId.asInt();
log.debug("Found folderId {} for uploadId {}", result, uploadId);
return result;
} catch (RestClientException e) {
log.error("Error while getting the folder details for upload id.", e, uploadId);
log.error("Error while getting folder details for uploadId {}: {}", uploadId, e.getMessage());
log.debug("Detailed error:", e);
return -1;
}
}
Expand Down Expand Up @@ -482,33 +582,59 @@ public int getUploadId(String shaValue, String fileName) {
HttpEntity<List<Map<String, String>>> requestEntity = new HttpEntity<>(body, headers);

try {
log.debug("Searching for file with SHA1 {} in Fossology at {}", shaValue, baseUrl + "filesearch");
ResponseEntity<JsonNode> response = restTemplate.exchange(baseUrl + "filesearch",
HttpMethod.POST, requestEntity, JsonNode.class);

JsonNode responseBody = response.getBody();
JsonNode firstElement = responseBody.get(0);
if (responseBody == null || !responseBody.isArray() || responseBody.size() == 0) {
log.error("Invalid or empty response when searching for file with SHA1 {}", shaValue);
return -1;
}

JsonNode firstElement = responseBody.get(0);
if (!firstElement.has("uploads")) {
log.warn("File with SHA1 {} not found in Fossology", shaValue);
return -1;
}

JsonNode uploads = firstElement.get("uploads");
List<String> uploadIdList = new ArrayList<>();
if (uploads != null && uploads.isArray()) {
for (JsonNode upload : uploads) {
uploadIdList.add(upload.asText());
}
uploadIdList.sort(Collections.reverseOrder());
log.debug("Found {} upload(s) for file with SHA1 {}", uploadIdList.size(), shaValue);
} else {
log.warn("No uploads found for file with SHA1 {}", shaValue);
return -1;
}

for (String uploadId : uploadIdList) {
int id = getFolderId(uploadId);
if (id != -1 && id == Integer.parseInt(folderId)) {
lastUploadedValue = Integer.parseInt(uploadId);
log.debug("Found existing upload (id={}) in target folder for file {}", lastUploadedValue, fileName);
break;
}
}

if (lastUploadedValue == -1) {
log.info("No existing upload found in target folder for file {} with SHA1 {}", fileName, shaValue);
}

return lastUploadedValue;
} catch (RestClientException e) {
log.error("Error while uploading file .", e, fileName);
log.error("Error while searching for file {} with SHA1 {}: {}", fileName, shaValue, e.getMessage());
log.debug("Detailed search error:", e);
return -1;
} catch (NumberFormatException e) {
log.error("Error parsing folder ID or upload ID: {}", e.getMessage());
return -1;
} catch (Exception e) {
log.error("Unexpected error while searching for file {}: {}", fileName, e.getMessage());
log.debug("Detailed error:", e);
return -1;
}
}
Expand Down

0 comments on commit 8f88e8a

Please sign in to comment.