GitUtil.java
package com.jsql.util;
import com.jsql.model.InjectionModel;
import org.apache.commons.lang3.SystemUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
/**
* Utility class used to connect to GitHub Rest webservices.
* It uses jsql-robot profile to post data to GitHub.
*/
public class GitUtil {
/**
* Log4j logger sent to view.
*/
private static final Logger LOGGER = LogManager.getRootLogger();
/**
* Application useful information as json object from GitHub repository.
* Used to get current development version and community news.
*/
private JSONObject jsonObject;
/**
* Define explicit labels to declare method parameters.
* Used for code readability only.
*/
public enum ShowOnConsole {
YES,
NO
}
private final InjectionModel injectionModel;
public GitUtil(InjectionModel injectionModel) {
this.injectionModel = injectionModel;
}
/**
* Verify if application is up-to-date against the version on GitHub.
* @param displayUpdateMessage YES for manual update verification, hidden otherwise
*/
public void checkUpdate(ShowOnConsole displayUpdateMessage) {
if (displayUpdateMessage == ShowOnConsole.YES) {
LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("UPDATE_LOADING"));
}
try {
var versionGit = Float.parseFloat(this.getJSONObject().getString("version"));
if (versionGit > Float.parseFloat(this.injectionModel.getVersionJsql())) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> I18nUtil.valueByKey("UPDATE_NEW_VERSION"));
} else if (displayUpdateMessage == ShowOnConsole.YES) {
LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, () -> I18nUtil.valueByKey("UPDATE_UPTODATE"));
}
} catch (NumberFormatException | JSONException e) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, I18nUtil.valueByKey("UPDATE_EXCEPTION"));
}
}
/**
* Define the body of an issue to send to GitHub for an unhandled exception.
* It adds different system data to the body and remove sensible data like
* injection URL.
* @param threadName name of thread where the exception occurred
* @param throwable unhandled exception to report to GitHub
*/
public void sendUnhandledException(String threadName, Throwable throwable) {
var osMetadata = String.join(
"\n",
String.format(
"jSQL: v%s",
this.injectionModel.getVersionJsql()
),
String.format(
"Java: v%s-%s-%s on %s",
SystemUtils.JAVA_VERSION,
SystemUtils.OS_ARCH,
SystemUtils.USER_LANGUAGE,
SystemUtils.JAVA_RUNTIME_NAME
),
String.format(
"OS: %s (v%s)",
SystemUtils.OS_NAME, SystemUtils.OS_VERSION
),
String.format(
"Desktop: %s",
System.getProperty("sun.desktop") != null
? System.getProperty("sun.desktop")
: "undefined"
),
String.format(
"Strategy: %s",
this.injectionModel.getMediatorStrategy().getStrategy() != null
? this.injectionModel.getMediatorStrategy().getStrategy().getName()
: "undefined"
),
String.format(
"Db engine: %s",
this.injectionModel.getMediatorVendor().getVendor().toString()
)
);
var exceptionText = String.format(
"Exception on %s%n%s%n",
threadName,
ExceptionUtils.getStackTrace(throwable).trim()
);
var clientDescription = String.format(
"```yaml%n%s%n```%n```java%n%s```",
osMetadata,
exceptionText
);
clientDescription = clientDescription.replaceAll("(https?://[.a-zA-Z_0-9]*)+", org.apache.commons.lang3.StringUtils.EMPTY);
this.sendReport(clientDescription, ShowOnConsole.NO, "Unhandled "+ throwable.getClass().getSimpleName());
}
/**
* Connect to GitHub webservices and create an Issue on the repository.
* Used by translation protocol, unhandled exception detection and manual Issue reporting.
* @param reportBody text of the Issue
* @param showOnConsole in case of manual Issue reporting. Hidden in case of automatic reporting of unhandled exception.
* @param reportTitle title of the Issue
*/
public void sendReport(String reportBody, ShowOnConsole showOnConsole, String reportTitle) {
if (this.injectionModel.getMediatorUtils().getProxyUtil().isNotLive(showOnConsole)) {
return;
}
var httpRequest = HttpRequest.newBuilder()
.uri(URI.create(this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperties().getProperty("github.issues.url")))
.setHeader(
"Authorization",
"token "
+ StringUtil.base64Decode(
this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperties().getProperty("github.token")
)
)
.POST(BodyPublishers.ofString(
new JSONObject()
.put("title", reportTitle)
.put("body", reportBody)
.toString()
))
.timeout(Duration.ofSeconds(15))
.build();
try {
HttpResponse<String> response = this.injectionModel.getMediatorUtils().getConnectionUtil().getHttpClient().send(httpRequest, BodyHandlers.ofString());
this.readGithubResponse(response, showOnConsole);
} catch (InterruptedException | IOException e) {
if (showOnConsole == ShowOnConsole.YES) {
LOGGER.log(
LogLevelUtil.CONSOLE_ERROR,
String.format("Error during GitHub report connection: %s", e.getMessage())
);
}
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
}
}
private void readGithubResponse(HttpResponse<String> response, ShowOnConsole showOnConsole) throws IOException {
try {
// Read the response
String sourcePage = response.body();
if (showOnConsole == ShowOnConsole.YES) {
var jsonObjectResponse = new JSONObject(sourcePage);
var urlIssue = jsonObjectResponse.getString("html_url");
LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Sent to GitHub: {}", urlIssue);
}
} catch (Exception e) {
throw new IOException("Connection to the GitHub API failed, check your connection or update jSQL");
}
}
/**
* Displays news information on the console from GitHub web service.
* Infos concern the general roadmap for the application, current development status
* and other useful statements for the community.
*/
public void showNews() {
try {
var news = this.getJSONObject().getJSONArray("news");
for (var index = 0 ; index < news.length() ; index++) {
LOGGER.log(LogLevelUtil.CONSOLE_INFORM, news.get(index));
}
} catch (JSONException e) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Connection to the GitHub API failed");
}
}
/**
* Instantiate the jsonObject from json data if not already set.
* @return jsonObject describing json data
*/
public JSONObject getJSONObject() {
if (this.jsonObject == null) {
String json = this.injectionModel.getMediatorUtils().getConnectionUtil().getSource(
this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperties().getProperty("github.webservice.url")
);
// Fix #45349: JSONException on new JSONObject(json)
try {
this.jsonObject = new JSONObject(json);
} catch (JSONException e) {
try {
this.jsonObject = new JSONObject("{\"version\": \"0\", \"news\": []}");
} catch (JSONException eInner) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Fetching default JSON failed", eInner);
}
LOGGER.log(
LogLevelUtil.CONSOLE_ERROR,
"Fetching configuration from GitHub failed. Wait for service to be available, check your connection or update jSQL"
);
}
}
return this.jsonObject;
}
}