diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
new file mode 100644
index 00000000..3f0c5100
--- /dev/null
+++ b/.github/workflows/nightly.yml
@@ -0,0 +1,81 @@
+# This workflow will build and test all Gradle-migrated projects in specs-java-libs every day at midnight
+# It will also run on every push and pull request
+
+name: nightly
+
+on:
+ push:
+ workflow_dispatch:
+
+permissions:
+ checks: write
+ contents: write
+
+env:
+ JAVA_VERSION: 17
+
+jobs:
+ build-gradle-projects:
+ name: Build & Test Gradle Projects Sequentially
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout specs-java-libs
+ uses: actions/checkout@v4
+
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: ${{ env.JAVA_VERSION }}
+
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v4
+ with:
+ gradle-version: current
+ dependency-graph: generate-and-submit
+
+ - name: Build and test all Gradle projects sequentially
+ run: |
+ projects=(
+ AsmParser
+ CommonsCompressPlus
+ CommonsLangPlus
+ GearmanPlus
+ GitPlus
+ Gprofer
+ GsonPlus
+ GuiHelper
+ JacksonPlus
+ JadxPlus
+ JavaGenerator
+ jOptions
+ JsEngine
+ LogbackPlus
+ SpecsUtils
+ SymjaPlus
+ tdrcLibrary
+ XStreamPlus
+ )
+ failed=()
+ for project in "${projects[@]}"; do
+ echo "\n===== Building and testing $project ====="
+ cd "$project"
+ if ! gradle build test; then
+ echo "[ERROR] $project failed to build or test"
+ failed+=("$project")
+ fi
+ cd -
+ done
+ if [ ${#failed[@]} -ne 0 ]; then
+ echo "\nThe following projects failed: ${failed[*]}"
+ exit 1
+ fi
+ env:
+ GITHUB_DEPENDENCY_GRAPH_ENABLED: false
+
+ - name: Publish Test Reports
+ uses: mikepenz/action-junit-report@v4
+ if: always()
+ with:
+ report_paths: '**/build/test-results/test/TEST-*.xml'
+ summary: true
diff --git a/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java b/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java
index af49c55f..7f2db5c5 100644
--- a/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java
+++ b/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java
@@ -1,14 +1,14 @@
/**
* Copyright 2017 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.compress;
@@ -26,28 +26,59 @@
import pt.up.fe.specs.util.lazy.Lazy;
import pt.up.fe.specs.util.providers.StringProvider;
+/**
+ * Enum representing supported compression formats for file output.
+ *
+ * Provides methods to create compressors for ZIP and GZ formats and to retrieve formats by extension.
+ */
public enum ZipFormat implements StringProvider {
+ /** ZIP file format. */
ZIP("zip"),
+ /** GZ (GZip) file format. */
GZ("gz");
private static final Lazy> ENUM_HELPER = EnumHelperWithValue.newLazyHelperWithValue(ZipFormat.class);
private final String extension;
+ /**
+ * Creates a new ZipFormat with the given file extension.
+ *
+ * @param extension the file extension for the format
+ */
private ZipFormat(String extension) {
this.extension = extension;
}
+ /**
+ * Returns an Optional containing the ZipFormat corresponding to the given extension, if available.
+ *
+ * @param extension the file extension
+ * @return an Optional with the matching ZipFormat, or empty if not found
+ */
public static Optional fromExtension(String extension) {
return ENUM_HELPER.get().fromValueTry(extension);
}
+ /**
+ * Returns the string representation (file extension) of this format.
+ *
+ * @return the file extension
+ */
@Override
public String getString() {
return extension;
}
+ /**
+ * Creates a new file compressor OutputStream for the given filename and output stream, according to this format.
+ *
+ * @param filename the name of the file to compress (used for ZIP entries)
+ * @param outputStream the output stream to wrap
+ * @return a new OutputStream for the compressed file
+ * @throws RuntimeException if the compressor cannot be created
+ */
public OutputStream newFileCompressor(String filename, OutputStream outputStream) {
switch (this) {
case ZIP:
diff --git a/CommonsLangPlus/README.md b/CommonsLangPlus/README.md
new file mode 100644
index 00000000..09fc6fca
--- /dev/null
+++ b/CommonsLangPlus/README.md
@@ -0,0 +1,14 @@
+# CommonsLangPlus
+
+CommonsLangPlus is a utility library that provides additional wrappers and helpers around the Apache Commons Lang and Commons Text libraries. It offers platform detection utilities and string manipulation helpers to simplify common Java development tasks.
+
+## Features
+- Platform detection (Windows, Linux, Mac, Unix, ARM, CentOS)
+- String escaping utilities (HTML, etc.)
+- Lightweight and easy to use
+
+## Usage
+Add CommonsLangPlus to your Java project and use the static utility methods for platform checks and string operations.
+
+## License
+This project is licensed under the Apache License 2.0.
diff --git a/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java b/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java
index 6459c691..3619082d 100644
--- a/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java
+++ b/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java
@@ -1,22 +1,31 @@
/**
* Copyright 2017 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.lang;
import org.apache.commons.text.StringEscapeUtils;
+/**
+ * Utility class for Apache Commons Text string operations.
+ */
public class ApacheStrings {
+ /**
+ * Escapes HTML entities in the given string using Apache Commons Text.
+ *
+ * @param html the input HTML string
+ * @return the escaped HTML string
+ */
public static String escapeHtml(String html) {
return StringEscapeUtils.escapeHtml4(html);
}
diff --git a/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java b/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java
index 3514de1a..a0e04100 100644
--- a/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java
+++ b/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java
@@ -8,7 +8,7 @@
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.lang;
@@ -16,18 +16,20 @@
import org.apache.commons.lang3.SystemUtils;
/**
- * Wrappers around Apache commons-lang utility methods related to system platform identification.
- *
+ * Utility class providing wrappers around Apache commons-lang methods for system platform identification.
*
+ * Includes methods to check the current operating system and platform details.
+ *
* TODO: Rename to ApachePlatforms
*
* @author JoaoBispo
- *
*/
public class SpecsPlatforms {
/**
* Returns true if the operating system is a form of Windows.
+ *
+ * @return true if Windows OS, false otherwise
*/
public static boolean isWindows() {
return SystemUtils.IS_OS_WINDOWS;
@@ -35,61 +37,57 @@ public static boolean isWindows() {
/**
* Returns true if the operating system is a form of Linux.
+ *
+ * @return true if Linux OS, false otherwise
*/
public static boolean isLinux() {
return SystemUtils.IS_OS_LINUX;
}
/**
- * Returns true if the operating system is a form of Linux.
+ * Returns true if the operating system is Linux running on ARM architecture.
+ *
+ * @return true if Linux ARM, false otherwise
*/
public static boolean isLinuxArm() {
return SystemUtils.IS_OS_LINUX && "arm".equals(System.getProperty("os.arch").toLowerCase());
}
+ /**
+ * Returns true if the operating system version indicates CentOS 6.
+ *
+ * @return true if CentOS 6, false otherwise
+ */
public static boolean isCentos6() {
return System.getProperty("os.version").contains(".el6.");
}
+ /**
+ * Returns the name of the current platform/OS.
+ *
+ * @return the OS name
+ */
public static String getPlatformName() {
return SystemUtils.OS_NAME;
}
/**
- * Returns true if the operating system is a form of Linux or Solaris.
+ * Returns true if the operating system is a form of Unix (Linux or Solaris).
+ *
+ * @return true if Unix OS, false otherwise
*/
public static boolean isUnix() {
return SystemUtils.IS_OS_UNIX;
}
+ /**
+ * Returns true if the operating system is a form of Mac OS.
+ *
+ * @return true if Mac OS, false otherwise
+ */
public static boolean isMac() {
return SystemUtils.IS_OS_MAC;
}
- /*
- public static Process getShell() {
- String cmd = getShellCommand();
-
- ProcessBuilder builder = new ProcessBuilder(cmd);
-
- try {
- return builder.start();
- } catch (IOException e) {
- throw new RuntimeException("Could not start process " + cmd);
- }
- }
-
- public static String getShellCommand() {
- if (isWindows()) {
- return "cmd.exe /start";
- }
-
- if (isUnix()) {
- return "/bin/bash";
- }
-
- throw new RuntimeException("No shell defined for platform " + getPlatformName());
- }
- */
-
+ // TODO: Implement shell-related utilities if needed in the future.
}
diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java b/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java
index 8a091d6d..6142536d 100644
--- a/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java
+++ b/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java
@@ -1,14 +1,14 @@
/**
* Copyright 2016 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gearman;
@@ -20,35 +20,44 @@
import pt.up.fe.specs.util.SpecsLogs;
+/**
+ * Utility methods for creating and managing Gearman servers.
+ */
public class GearmanUtils {
/**
- * Creates a Gearman server on default port is 4730.
- *
- * @param gearman
- * @param args
- * @return
+ * Creates a Gearman server on the default port (4730) or connects to a remote server if an address is provided.
+ *
+ * @param gearman the Gearman instance
+ * @param args if not empty, the first element is used as the server address
+ * @return a GearmanServer instance
+ * @throws RuntimeException if the server cannot be started or connected
*/
public static GearmanServer newServer(final Gearman gearman, String[] args) {
return newServer(gearman, 4730, args);
}
+ /**
+ * Creates a Gearman server on the specified port or connects to a remote server if an address is provided.
+ *
+ * @param gearman the Gearman instance
+ * @param port the port to use
+ * @param args if not empty, the first element is used as the server address
+ * @return a GearmanServer instance
+ * @throws RuntimeException if the server cannot be started or connected
+ */
public static GearmanServer newServer(final Gearman gearman, int port, String[] args) {
-
try {
if (args.length > 0) {
String addr = args[0];
SpecsLogs.msgInfo("Connecting to Gearman Server on " + addr + ":" + port);
return gearman.createGearmanServer(addr, port);
}
-
SpecsLogs.msgInfo("Gearman Server listening on port " + port);
return gearman.startGearmanServer(port);
-
} catch (IOException e) {
throw new RuntimeException("Exception while trying to start Gearman Server:\n", e);
}
-
}
}
diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java
index b297a582..208234ce 100644
--- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java
+++ b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java
@@ -1,14 +1,14 @@
/**
* Copyright 2016 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gearman.specsworker;
@@ -21,6 +21,12 @@
import com.google.gson.GsonBuilder;
+/**
+ * Generic Gearman worker that delegates work to a provided function and builds error output using a function.
+ *
+ * This worker allows dynamic delegation of Gearman jobs to a user-supplied {@link GearmanFunction} and custom error output
+ * formatting using a {@link Function}.
+ */
public class GenericSpecsWorker extends SpecsWorker {
private final GearmanFunction function;
@@ -28,36 +34,70 @@ public class GenericSpecsWorker extends SpecsWorker {
private final String workerName;
+ /**
+ * Constructs a new GenericSpecsWorker.
+ *
+ * @param workerName the name of the worker
+ * @param function the Gearman function to delegate work to
+ * @param outputBuilder a function to build error output objects from error messages
+ * @param timeout the timeout value
+ * @param timeUnit the time unit for the timeout
+ */
public GenericSpecsWorker(String workerName, GearmanFunction function, Function outputBuilder,
long timeout,
TimeUnit timeUnit) {
super(timeout, timeUnit);
-
this.workerName = workerName;
this.function = function;
this.outputBuilder = outputBuilder;
}
+ /**
+ * Returns the name of this worker.
+ *
+ * @return the worker name
+ */
@Override
public String getWorkerName() {
return workerName;
}
+ /**
+ * Setup hook called before work execution. Default implementation does nothing.
+ */
@Override
public void setUp() {
-
+ // No setup required by default
}
+ /**
+ * Teardown hook called after work execution. Default implementation does nothing.
+ */
@Override
public void tearDown() {
-
+ // No teardown required by default
}
+ /**
+ * Delegates the work to the provided Gearman function.
+ *
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
+ * @return the result of the delegated function
+ * @throws Exception if the delegated function throws an exception
+ */
@Override
public byte[] workInternal(String function, byte[] data, GearmanFunctionCallback callback) throws Exception {
return this.function.work(function, data, callback);
}
+ /**
+ * Builds the error output using the provided outputBuilder function and serializes it as JSON.
+ *
+ * @param message the error message
+ * @return the error output as JSON bytes
+ */
@Override
protected byte[] getErrorOutput(String message) {
return new GsonBuilder().create().toJson(outputBuilder.apply(message)).getBytes();
diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java
index b452addc..f7dee337 100644
--- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java
+++ b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java
@@ -1,14 +1,14 @@
/**
* Copyright 2016 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gearman.specsworker;
@@ -24,45 +24,76 @@
import pt.up.fe.specs.util.SpecsLogs;
+/**
+ * Abstract Gearman worker that handles JSON input/output using Gson.
+ *
+ * @param the input type
+ * @param the output type
+ */
public abstract class JsonSpecsWorker extends SpecsWorker {
private final Gson gson;
private final Class inputClass;
- // private final Class outputClass;
- // public TypedSpecsWorker(Class inputClass, Class outputClass, long timeout, TimeUnit timeUnit) {
+ /**
+ * Constructs a JsonSpecsWorker with the given input class and timeout settings.
+ *
+ * @param inputClass the class of the input type
+ * @param timeout the timeout duration
+ * @param timeUnit the time unit for the timeout
+ */
public JsonSpecsWorker(Class inputClass, long timeout, TimeUnit timeUnit) {
super(timeout, timeUnit);
-
this.inputClass = inputClass;
- // this.outputClass = outputClass;
this.gson = new GsonBuilder().create();
}
+ /**
+ * Handles the work by deserializing input, invoking the worker, and serializing the result as JSON.
+ *
+ * @param function the function name
+ * @param data the input data as bytes
+ * @param callback the Gearman callback
+ * @return the result as JSON bytes
+ * @throws Exception if processing fails
+ */
@Override
public byte[] workInternal(String function, byte[] data, GearmanFunctionCallback callback) throws Exception {
-
- // Type typeOfT = new TypeToken() {
- // }.getType();
-
- // I parsedData = gson.fromJson(new String(data), typeOfT);
I parsedData = gson.fromJson(new String(data), inputClass);
O result = workInternal(function, parsedData, callback);
-
// Print time-stamp
var time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
SpecsLogs.info("Finished job '" + this.getClass().getName() + "' at " + time);
-
return gson.toJson(result).getBytes();
}
+ /**
+ * Performs the actual work using the deserialized input.
+ *
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
+ * @return the result
+ */
public abstract O workInternal(String function, I data, GearmanFunctionCallback callback);
+ /**
+ * Returns the error output as JSON bytes.
+ *
+ * @param message the error message
+ * @return the error output as JSON bytes
+ */
@Override
protected byte[] getErrorOutput(String message) {
return gson.toJson(getTypedErrorOutput(message)).getBytes();
}
+ /**
+ * Returns a typed error output object for the given message.
+ *
+ * @param message the error message
+ * @return the error output object
+ */
protected abstract O getTypedErrorOutput(String message);
}
diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java
index 03c26e34..345fc663 100644
--- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java
+++ b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java
@@ -1,14 +1,14 @@
/**
* Copyright 2013 SPeCS Research Group.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gearman.specsworker;
@@ -30,116 +30,109 @@
import pt.up.fe.specs.util.SpecsStrings;
/**
+ * Abstract base class for Gearman workers with timeout, setup/teardown, and error handling support.
+ *
+ * Provides a framework for running Gearman jobs with configurable timeouts, setup/teardown hooks, and error reporting.
+ * Subclasses should implement {@link #workInternal(String, byte[], GearmanFunctionCallback)} and
+ * {@link #getErrorOutput(String)} for custom job logic and error output.
+ *
* @author Joao Bispo
- *
*/
public abstract class SpecsWorker implements GearmanFunction {
private final long timeout;
private final TimeUnit timeUnit;
+ /**
+ * Constructs a SpecsWorker with the given timeout and time unit.
+ *
+ * @param timeout the timeout value for job execution
+ * @param timeUnit the time unit for the timeout
+ */
public SpecsWorker(long timeout, TimeUnit timeUnit) {
-
this.timeout = timeout;
this.timeUnit = timeUnit;
}
- // public SpecsWorker() {
- // this(SPeCSGearman.getTimeout(), SPeCSGearman.getTimeunit());
- // }
-
/**
- * @return the timeout
+ * Returns the timeout value for job execution.
+ *
+ * @return the timeout value
*/
public long getTimeout() {
return timeout;
}
/**
- * @return the timeUnit
+ * Returns the time unit for the timeout value.
+ *
+ * @return the time unit
*/
public TimeUnit getTimeUnit() {
return timeUnit;
}
/**
- * As default, returns the simple name of the class.
- *
- * @return
+ * Returns the name of this worker. By default, returns the simple class name.
+ *
+ * @return the worker name
*/
public String getWorkerName() {
return getClass().getSimpleName();
}
- /* (non-Javadoc)
- * @see org.gearman.GearmanFunction#work(java.lang.String, byte[], org.gearman.GearmanFunctionCallback)
+ /**
+ * Executes the Gearman job, calling setup and teardown hooks.
+ *
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
+ * @return the result of the job
+ * @throws Exception if job execution fails
*/
@Override
public byte[] work(String function, byte[] data, GearmanFunctionCallback callback)
throws Exception {
-
- // if (name.endsWith("WithTimeout")) {
- // name = name.substring(0, name.length() - "WithTimeout".length());
- // }
-
- // long executeStart = System.nanoTime();
- // SetUp
setUp();
- // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Setup: ", tic));
-
byte[] result = execute(function, data, callback);
-
- // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Execution: ", executeStart));
-
- // TearDown
- // tic = System.nanoTime();
tearDown();
-
- // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Teardown: ", tic));
- // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Total: ", workStart));
-
return result;
}
/**
- * @param function
- * @param data
- * @param callback
- * @return
- * @throws InterruptedException
- * @throws ExecutionException
+ * Executes the job with timeout and error handling, writing input/output to files if outputDir is set.
+ *
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
+ * @return the result of the job
+ * @throws InterruptedException if execution is interrupted
+ * @throws ExecutionException if execution fails
*/
private byte[] execute(String function, byte[] data, GearmanFunctionCallback callback)
throws InterruptedException, ExecutionException {
-
File outputDir = getOutputDir();
-
- // Write input data, for debug
if (outputDir != null) {
SpecsIo.write(new File(outputDir, "input_data.json"), new String(data));
}
-
byte[] result = executeInternal(function, data, callback);
-
- // Write output data, for debug
if (outputDir != null) {
SpecsIo.write(new File(outputDir, "output_data.json"), new String(result));
}
-
- // Create timeout output
- // if (result == null) {
- // result = getErrorOutput();
- // }
return result;
}
+ /**
+ * Executes the job in a separate thread, enforcing the timeout and handling exceptions.
+ *
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
+ * @return the result of the job
+ */
public byte[] executeInternal(String function, byte[] data, GearmanFunctionCallback callback) {
- // Launch work in another thread
ExecutorService executor = Executors.newSingleThreadExecutor();
-
- // Future future = executor.submit(new Task(gearmanFunction, data, callback));
TaskV2 task = new TaskV2(this, function, data, callback);
- // Future future = executor.submit(new Task(function, data, callback));
Future future = executor.submit(task);
byte[] result = null;
try {
@@ -147,114 +140,108 @@ public byte[] executeInternal(String function, byte[] data, GearmanFunctionCallb
String id = callback != null ? new String(callback.getJobHandle()) : "";
SpecsLogs.msgInfo("[SpecsWorker] Starting '" + name + "' (" + id + " -> "
+ SpecsIo.getWorkingDir().getAbsolutePath() + ")");
-
- // LoggingUtils.msgLib("Started task with a timeout of " + timeout + " " + timeUnit);
long workStart = System.nanoTime();
result = future.get(timeout, timeUnit);
long workEnd = System.nanoTime();
- // LoggingUtils.msgLib("Finished task in the alloted time");
SpecsLogs.msgInfo("[SpecsWorker] Finished '" + getWorkerName() + "', "
+ SpecsStrings.parseTime(workEnd - workStart) + " (id " + id + ")");
} catch (TimeoutException e) {
SpecsLogs.warn("[SpecsWorker] Timeout during worker execution", e);
- // future.cancel(true);
future.cancel(true);
SpecsLogs.msgInfo("Worker [" + Thread.currentThread().getName() + "]: putting thread/task to sleep... ");
task.interrupt();
- // executor.shutdownNow();
- // return getErrorOutput(getTimeoutMessage());
result = getErrorOutput(getTimeoutMessage());
} catch (Exception e) {
SpecsLogs.warn("[SpecsWorker] Exception during worker execution", e);
future.cancel(true);
task.interrupt();
- // executor.shutdownNow();
- // return getErrorOutput(e.getMessage());
result = getErrorOutput(e.getMessage());
- // return getErrorOutput();
}
-
executor.shutdownNow();
return result;
}
+ /**
+ * Returns a timeout message for error reporting.
+ *
+ * @return the timeout message
+ */
private String getTimeoutMessage() {
return "Terminated task after exceeding the maximum alloted time of " + getTimeout()
+ SpecsStrings.getTimeUnitSymbol(getTimeUnit());
}
/**
- * Executes before the main work. Default implementation does nothing.
+ * Setup hook called before job execution. Default implementation does nothing.
*/
public void setUp() {
-
+ // No setup required by default
}
- /*
- public void sleep() {
- try {
- SpecsLogs.msgInfo("Task [" + Thread.currentThread().getName() + "] Have been put to sleep... ");
- Thread.sleep(2000);
- SpecsLogs.msgInfo("Task [" + Thread.currentThread().getName() + "] Awake!");
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt(); // set interrupt flag
- SpecsLogs.warn("Interrupted:\n", e);
- }
- }
- */
-
/**
- * Executes after the main work. Default implementation does nothing.
+ * Teardown hook called after job execution. Default implementation does nothing.
*/
public void tearDown() {
-
+ // No teardown required by default
}
+ /**
+ * Performs the actual work for the Gearman job. Must be implemented by subclasses.
+ *
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
+ * @return the result of the job
+ * @throws Exception if job execution fails
+ */
public abstract byte[] workInternal(String function, byte[] data,
GearmanFunctionCallback callback) throws Exception;
+ /**
+ * Returns the error output for a given error message. Must be implemented by subclasses.
+ *
+ * @param message the error message
+ * @return the error output as bytes
+ */
protected abstract byte[] getErrorOutput(String message);
+ /**
+ * Task for executing a Gearman job in a separate thread.
+ */
class Task implements Callable {
-
private final String function;
private final byte[] data;
private final GearmanFunctionCallback callback;
/**
- * @param gearmanFunction
- * @param data
- * @param callback
+ * Constructs a Task for the given function, data, and callback.
+ *
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
*/
- /*
- public Task(String gearmanFunction, byte[] data, GearmanFunctionCallback callback) {
- this.function = gearmanFunction;
- this.data = data;
- this.callback = callback;
- }
- */
-
public Task(String function, byte[] data, GearmanFunctionCallback callback) {
-
this.function = function;
this.data = data;
this.callback = callback;
-
}
+ /**
+ * Calls the workInternal method to perform the job.
+ *
+ * @return the result of the job
+ * @throws Exception if job execution fails
+ */
@Override
public byte[] call() throws Exception {
- // return workInternal(gearmanFunction, data, callback);
return workInternal(function, data, callback);
}
-
- // public static void sleep() {
- // Thread.sleep(2000);
- // }
}
+ /**
+ * TaskV2 for executing a Gearman job in a separate thread, with thread interruption support.
+ */
static class TaskV2 implements Callable {
-
private final SpecsWorker worker;
private final String function;
private final byte[] data;
@@ -262,26 +249,26 @@ static class TaskV2 implements Callable {
private Thread taskThread = null;
/**
- * @param gearmanFunction
- * @param data
- * @param callback
+ * Constructs a TaskV2 for the given worker, function, data, and callback.
+ *
+ * @param worker the SpecsWorker instance
+ * @param function the function name
+ * @param data the input data
+ * @param callback the Gearman callback
*/
- /*
- public Task(String gearmanFunction, byte[] data, GearmanFunctionCallback callback) {
- this.function = gearmanFunction;
- this.data = data;
- this.callback = callback;
- }
- */
-
public TaskV2(SpecsWorker worker, String function, byte[] data, GearmanFunctionCallback callback) {
this.worker = worker;
this.function = function;
this.data = data;
this.callback = callback;
-
}
+ /**
+ * Calls the worker's workInternal method to perform the job, tracking the thread for interruption.
+ *
+ * @return the result of the job
+ * @throws Exception if job execution fails
+ */
@Override
public byte[] call() throws Exception {
taskThread = Thread.currentThread();
@@ -290,20 +277,27 @@ public byte[] call() throws Exception {
SpecsLogs.msgInfo("Finished task in thread " + taskThread.getName());
taskThread = null;
return result;
- // return workInternal(gearmanFunction, data, callback);
- // return workInternal(function, data, callback);
}
+ /**
+ * Returns the SpecsWorker associated with this task.
+ *
+ * @return the SpecsWorker instance
+ */
public SpecsWorker getWorker() {
return worker;
}
+ /**
+ * Interrupts the running thread for this task, waiting 2 seconds for cleanup.
+ *
+ * If the thread is still alive after interruption, logs a warning.
+ */
public void interrupt() {
if (taskThread == null) {
SpecsLogs.msgInfo("Task.sleep(): No thread set, returning");
return;
}
-
SpecsLogs.msgInfo("Interrupting task in thread " + taskThread.getName() + ", waiting 2 seconds");
taskThread.interrupt();
try {
@@ -321,21 +315,16 @@ public void interrupt() {
// https://stackoverflow.com/questions/24855182/interrupt-java-thread-running-nashorn-script#
taskThread.stop();
} else {
- SpecsLogs.msgInfo("Thread " + taskThread.getName() + "died gracefully");
+ SpecsLogs.msgInfo("Thread " + taskThread.getName() + " died gracefully");
}
-
- /*
- try {
- SpecsLogs.msgInfo("Task in thread " + taskThread.getName() + " awake!");
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt(); // set interrupt flag
- SpecsLogs.warn("Interrupted:\n", e);
- }
- */
}
-
}
+ /**
+ * Returns the output directory for input/output debug files. Default is null (no output).
+ *
+ * @return the output directory, or null if not set
+ */
public File getOutputDir() {
return null;
}
diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java b/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java
index a3c6f35f..fe3abd1a 100644
--- a/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java
+++ b/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java
@@ -1,36 +1,54 @@
/**
* Copyright 2016 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gearman.utils;
import java.security.Permission;
+/**
+ * Security manager for Gearman workers that blocks System.exit() calls and allows all other permissions.
+ *
+ * This class is intended to prevent accidental JVM termination by capturing calls to System.exit().
+ * All other permission checks are allowed.
+ */
public class GearmanSecurityManager extends SecurityManager {
+ /**
+ * Allows all permissions (no restrictions).
+ */
@Override
public void checkPermission(Permission perm) {
- // allow anything.
+ // allow anything.
}
+ /**
+ * Allows all permissions (no restrictions).
+ */
@Override
public void checkPermission(Permission perm, Object context) {
- // allow anything.
+ // allow anything.
}
+ /**
+ * Throws a SecurityException when System.exit() is called, preventing JVM termination.
+ *
+ * @param status the exit status
+ * @throws SecurityException always
+ */
@Override
public void checkExit(int status) {
- super.checkExit(status);
- throw new SecurityException("Captured call to System.exit(" + status + ")");
+ super.checkExit(status);
+ throw new SecurityException("Captured call to System.exit(" + status + ")");
}
}
diff --git a/GitPlus/README.md b/GitPlus/README.md
new file mode 100644
index 00000000..141fbe98
--- /dev/null
+++ b/GitPlus/README.md
@@ -0,0 +1,15 @@
+# GitPlus
+
+GitPlus is a Java library that provides utility classes and methods for interacting with Git repositories using the JGit library. It simplifies common Git operations such as cloning, checking out branches or commits, and managing multiple repositories programmatically.
+
+## Features
+- Clone and manage multiple Git repositories
+- Checkout branches and commits
+- Utility methods for repository folder management
+- Enum-based URL options for advanced repository handling
+
+## Usage
+Add GitPlus to your Java project and use the provided static utility methods and classes for Git operations.
+
+## License
+This project is licensed under the Apache License 2.0.
diff --git a/GitPlus/src/pt/up/fe/specs/git/GitRepo.java b/GitPlus/src/pt/up/fe/specs/git/GitRepo.java
index 73eb0341..21af0f4f 100644
--- a/GitPlus/src/pt/up/fe/specs/git/GitRepo.java
+++ b/GitPlus/src/pt/up/fe/specs/git/GitRepo.java
@@ -1,14 +1,14 @@
/**
* Copyright 2022 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.git;
@@ -28,6 +28,10 @@
import pt.up.fe.specs.util.SpecsIo;
import pt.up.fe.specs.util.SpecsLogs;
+/**
+ * Represents a local clone of a remote Git repository, with support for options such as commit and folder selection.
+ * Provides methods for preparing, cloning, and updating the repository.
+ */
public class GitRepo {
private final Map options;
@@ -35,6 +39,12 @@ public class GitRepo {
private final File repoFolder;
private final File workFolder;
+ /**
+ * Creates a new GitRepo instance with the given repository URL and options.
+ *
+ * @param repoUrl the URL of the remote repository
+ * @param options a map of options for the repository, such as commit and folder
+ */
public GitRepo(String repoUrl, Map options) {
this.repoUrl = repoUrl;
this.options = options;
@@ -52,6 +62,11 @@ public GitRepo(String repoUrl, Map options) {
this.workFolder = createWorkFolder();
}
+ /**
+ * Creates the working folder for the repository based on the options provided.
+ *
+ * @return the working folder
+ */
private File createWorkFolder() {
// Check folder option
var foldername = options.get(GitUrlOption.FOLDER);
@@ -71,10 +86,21 @@ private File createWorkFolder() {
return workFolder;
}
+ /**
+ * Creates a new GitRepo instance with the given repository URL and no options.
+ *
+ * @param repoUrl the URL of the remote repository
+ */
public GitRepo(String repoUrl) {
this(repoUrl, Collections.emptyMap());
}
+ /**
+ * Creates a new GitRepo instance from a repository path.
+ *
+ * @param repositoryPath the path to the repository
+ * @return a new GitRepo instance
+ */
public static GitRepo newInstance(String repositoryPath) {
SpecsLogs.msgInfo("Processing git url '" + repositoryPath + "'");
@@ -116,6 +142,11 @@ public File getWorkFolder() {
return workFolder;
}
+ /**
+ * Creates the folder for the repository based on its name and options.
+ *
+ * @return the repository folder
+ */
private File createRepoFolder() {
// Get repo name
@@ -133,6 +164,12 @@ private File createRepoFolder() {
return new File(eclipseBuildFolder, repoName);
}
+ /**
+ * Parses the repository URL and extracts options.
+ *
+ * @param repositoryPath the repository URL
+ * @return a map of options extracted from the URL
+ */
private static Map parseUrl(String repositoryPath) {
var urlStringOptions = SpecsIo.parseUrl(repositoryPath)
.map(url -> SpecsIo.parseUrlQuery(url))
@@ -170,11 +207,6 @@ private void prepareRepo() {
// Cloning repo from scratch, no pull needed
clonedRepo = true;
- // Check if there is a login/pass in the url
- // CredentialsProvider cp = getCredentials(repositoryPath);
- // System.out.println("SETTING NULL");
- // clone.setCredentialsProvider(null);
- // clone.call();
} catch (GitAPIException e) {
throw new RuntimeException("Could not clone repository '" + repoUrl + "'", e);
}
@@ -205,14 +237,6 @@ private void prepareRepo() {
// If the repo was just cloned, checkout the commit
if (clonedRepo) {
- // First, fetch branches
- // SpecsLogs.info("Fetching branches");
- // var fetchResult = repo.fetch()
- // .setRefSpecs(new RefSpec("refs/heads/" + branch))
- // .call();
-
- // System.out.println("FETCH RESULT: " + fetchResult.getTrackingRefUpdates());
-
// Only checkout if not in the correct branch
String currentBranch = getCurrentBranch(repo);
@@ -256,69 +280,14 @@ private void prepareRepo() {
if (isBranchName) {
pull(repo, commit);
}
- /*
- // If branch, checkout branch
-
- var branch = urlOptions.get(GitUrlOption.BRANCH);
-
- String currentBranch = null;
- try {
- currentBranch = repo.getRepository().getBranch();
- } catch (IOException e) {
- throw new RuntimeException("Could not get current branch", e);
- }
-
- if (branch != null) {
- try {
-
- // First, fetch branches
- // SpecsLogs.info("Fetching branches");
- // var fetchResult = repo.fetch()
- // .setRefSpecs(new RefSpec("refs/heads/" + branch))
- // .call();
-
- // System.out.println("FETCH RESULT: " + fetchResult.getTrackingRefUpdates());
-
- // Only checkout if not in the correct branch
- if (!branch.equals(currentBranch)) {
- SpecsLogs.msgInfo("Checking out branch '" + branch + "'");
- repo.checkout()
- .setCreateBranch(true)
- .setName(branch)
- // .setStartPoint(commit)
- .call();
- } else {
- SpecsLogs.msgInfo("Already in branch '" + branch + "'");
- }
- } catch (GitAPIException e) {
- throw new RuntimeException(
- "Could not checkout branch '" + branch + "' in folder '" + repoFolder.getAbsolutePath() + "'",
- e);
- }
- }
-
- var commit = urlOptions.get(GitUrlOption.COMMIT);
- if (commit != null) {
-
- try {
- repo.checkout()
- .setCreateBranch(true)
- .setName(commit)
- // .setStartPoint(commit)
- .call();
- } catch (GitAPIException e) {
- throw new RuntimeException(
- "Could not checkout commit '" + commit + "' in folder '" + repoFolder.getAbsolutePath() + "'",
- e);
- }
- // (new RevWalk(repo, 0).parseCommit(null)
- }
-
- return needsPull;
- */
-
}
+ /**
+ * Gets the current branch of the repository.
+ *
+ * @param repo the Git repository
+ * @return the name of the current branch
+ */
private String getCurrentBranch(Git repo) {
String currentBranch = null;
try {
@@ -329,6 +298,9 @@ private String getCurrentBranch(Git repo) {
return currentBranch;
}
+ /**
+ * Checks for problems in the repository folder and cleans it if necessary.
+ */
private void checkRepoProblems() {
// If folder only contains a single .git folder, something might have gone wrong, delete folder
@@ -342,11 +314,10 @@ private void checkRepoProblems() {
}
/**
- * TODO: cleanRepoUrl here is needed because getRepoFolder receives the Query params, to avoid parsing them again.
- * If there is a class just for a single repo url, this could be managed differently.
- *
- * @param cleanRepoUrl
- * @param repo
+ * Pulls the latest changes from the remote repository.
+ *
+ * @param repo the Git repository
+ * @param branch the branch to pull, or null for the default branch
*/
private void pull(Git repo, String branch) {
try {
diff --git a/GitPlus/src/pt/up/fe/specs/git/GitRepos.java b/GitPlus/src/pt/up/fe/specs/git/GitRepos.java
index e2bb9bfa..8d28da93 100644
--- a/GitPlus/src/pt/up/fe/specs/git/GitRepos.java
+++ b/GitPlus/src/pt/up/fe/specs/git/GitRepos.java
@@ -1,14 +1,14 @@
/**
* Copyright 2017 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.git;
@@ -18,21 +18,26 @@
import java.util.concurrent.ConcurrentHashMap;
/**
+ * Utility class for managing multiple Git repositories within the JVM.
+ * Provides methods to retrieve and cache repository instances by path.
+ *
* TODO: Candidate for using GitManager.
- *
- * @author Joao Bispo
*
+ * @author Joao Bispo
*/
public class GitRepos {
- // Maps repo names to the folder where they are in the system
- // One instance per JVM
+ /**
+ * Maps repo names to the folder where they are in the system. One instance per JVM.
+ */
private static final Map REPOS = new ConcurrentHashMap<>();
- // public GitRepos() {
- //
- // }
-
+ /**
+ * Returns a {@link GitRepo} instance for the given repository path. If not cached, creates and caches a new instance.
+ *
+ * @param repositoryPath the path to the repository
+ * @return the GitRepo instance
+ */
public static GitRepo getRepo(String repositoryPath) {
var repo = REPOS.get(repositoryPath);
@@ -44,19 +49,13 @@ public static GitRepo getRepo(String repositoryPath) {
return repo;
}
+ /**
+ * Returns the working folder for the given repository path.
+ *
+ * @param repositoryPath the path to the repository
+ * @return the working folder as a File
+ */
public File getFolder(String repositoryPath) {
return getRepo(repositoryPath).getWorkFolder();
- // Get repo name
- // String repoName = SpecsGit.getRepoName(repositoryPath);
-
- // Check if it is already in the map
- // File repoFolder = repos.get(repoName);
- // var repo = REPOS.get(repositoryPath);
- // if (repo == null) {
- // repo = GitRepo.newInstance(repositoryPath);
- // REPOS.put(repositoryPath, repo);
- // }
- //
- // return repoFolder;
}
}
diff --git a/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java b/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java
index bf3ccba5..25d1faf3 100644
--- a/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java
+++ b/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java
@@ -1,28 +1,30 @@
/**
* Copyright 2022 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.git;
import pt.up.fe.specs.util.providers.StringProvider;
+/**
+ * Enum representing options that can be specified in a Git repository URL.
+ */
public enum GitUrlOption implements StringProvider {
- // BRANCH,
/**
- * A commit or branch that should be used
+ * A commit or branch that should be used.
*/
COMMIT,
/**
- * A folder inside the repository
+ * A folder inside the repository.
*/
FOLDER;
@@ -32,9 +34,13 @@ private GitUrlOption() {
this.option = name().toLowerCase();
}
+ /**
+ * Returns the string representation of the option.
+ *
+ * @return the option as a string
+ */
@Override
public String getString() {
return option;
}
-
}
diff --git a/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java b/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java
index e7bf3bcb..c1ce6d9e 100644
--- a/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java
+++ b/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java
@@ -40,9 +40,10 @@
import pt.up.fe.specs.util.SpecsLogs;
/**
+ * Utility class for Git operations using JGit.
+ * Provides methods for cloning, checking out, and managing Git repositories.
*
* @author JoaoBispo
- *
*/
public class SpecsGit {
@@ -50,6 +51,13 @@ public class SpecsGit {
private static final String URL_PARAM_BRANCH = "branch";
+ /**
+ * Parses a repository URL and returns the folder where the repository is located.
+ * If the repository does not exist locally, it will be cloned.
+ *
+ * @param repositoryPath the URL of the repository
+ * @return the folder where the repository is located
+ */
public static File parseRepositoryUrl(String repositoryPath) {
var repoFolder = parseRepositoryUrl(repositoryPath, true);
@@ -75,14 +83,16 @@ public static File parseRepositoryUrl(String repositoryPath) {
return repoFolder;
}
+ /**
+ * Checks out the specified branch in the given Git repository.
+ *
+ * @param git the Git repository
+ * @param branchName the name of the branch to check out
+ */
public static void checkout(Git git, String branchName) {
try {
- // System.out.println("SETTING TO BRANCH " + branch);
-
var currentBranch = git.getRepository().getBranch();
- // System.out.println("CURRENT BRANCH: " + currentBranch);
-
// Checkout branch
if (!branchName.equals(currentBranch)) {
@@ -98,18 +108,6 @@ public static void checkout(Git git, String branchName) {
git.checkout().setCreateBranch(createBranch)
.setName(branchName)
.call();
-
- // var currentFullBranch = git.getRepository().getFullBranch();
- // var startPoint = currentFullBranch.substring(0, currentFullBranch.length() -
- // currentBranch.length())
- // + branch;
- //
- // git.checkout().setCreateBranch(true)
- // .setName(branch)
- // .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
- // // .setStartPoint(startPoint)
- // .call();
-
}
} catch (Exception e) {
throw new RuntimeException(
@@ -117,17 +115,14 @@ public static void checkout(Git git, String branchName) {
}
}
- // public static String getBranchName(Git git) {
- // try {
- // var fullBranch = git.getRepository().getFullBranch();
- // var lastSlash = fullBranch.lastIndexOf('/');
- // return lastSlash != -1 ? fullBranch.substring(lastSlash + 1, fullBranch.length()) : fullBranch;
- // } catch (IOException e) {
- // throw new RuntimeException("Could not get the name of the branch of the git repository " + git, e);
- // }
- //
- // }
-
+ /**
+ * Parses a repository URL and returns the folder where the repository is located.
+ * If the repository does not exist locally, it will be cloned.
+ *
+ * @param repositoryPath the URL of the repository
+ * @param firstTime whether this is the first attempt to parse the repository URL
+ * @return the folder where the repository is located
+ */
private static File parseRepositoryUrl(String repositoryPath, boolean firstTime) {
String repoName = getRepoName(repositoryPath);
@@ -145,11 +140,6 @@ private static File parseRepositoryUrl(String repositoryPath, boolean firstTime)
.setDirectory(repoFolder)
.setCredentialsProvider(getCredentials(repositoryPath))
.call();
- // Check if there is a login/pass in the url
- // CredentialsProvider cp = getCredentials(repositoryPath);
- // System.out.println("SETTING NULL");
- // clone.setCredentialsProvider(null);
- // clone.call();
return repoFolder;
} catch (GitAPIException e) {
@@ -158,7 +148,6 @@ private static File parseRepositoryUrl(String repositoryPath, boolean firstTime)
}
// Repository already exists, pull
-
try {
SpecsLogs.msgInfo("Pulling repo '" + repositoryPath + "' in folder '" + repoFolder + "'");
Git gitRepo = Git.open(repoFolder);
@@ -183,6 +172,12 @@ private static File parseRepositoryUrl(String repositoryPath, boolean firstTime)
return repoFolder;
}
+ /**
+ * Retrieves the credentials provider for the given repository URL.
+ *
+ * @param repositoryPath the URL of the repository
+ * @return the credentials provider, or null if no credentials are required
+ */
public static CredentialsProvider getCredentials(String repositoryPath) {
var currentString = repositoryPath;
@@ -201,10 +196,7 @@ public static CredentialsProvider getCredentials(String repositoryPath) {
currentString = currentString.substring(0, firstSlashIndex);
}
- // System.out.println("CURRENT INDEX: " + currentString);
-
// Check if there is a login/pass in the url
- // Look for last index, in case the login has an '@'
var atIndex = currentString.lastIndexOf('@');
// No login
@@ -213,7 +205,6 @@ public static CredentialsProvider getCredentials(String repositoryPath) {
}
var loginPass = currentString.substring(0, atIndex);
- // System.out.println("LOGIN PASS: " + loginPass);
// Split login pass
var colonIndex = loginPass.indexOf(':');
@@ -237,15 +228,26 @@ public static CredentialsProvider getCredentials(String repositoryPath) {
var login = loginPass.substring(0, colonIndex);
var pass = loginPass.substring(colonIndex + 1, loginPass.length());
- // System.out.println("LOGIN: " + login);
- // System.out.println("PASS: " + pass);
return new UsernamePasswordCredentialsProvider(login, pass);
}
+ /**
+ * Pulls the latest changes from the remote repository into the local repository.
+ *
+ * @param repoFolder the folder of the local repository
+ * @return the result of the pull operation
+ */
public static PullResult pull(File repoFolder) {
return pull(repoFolder, null);
}
+ /**
+ * Pulls the latest changes from the remote repository into the local repository.
+ *
+ * @param repoFolder the folder of the local repository
+ * @param cp the credentials provider
+ * @return the result of the pull operation
+ */
public static PullResult pull(File repoFolder, CredentialsProvider cp) {
try {
SpecsLogs.msgInfo("Pulling repo in folder '" + repoFolder + "'");
@@ -263,6 +265,12 @@ public static PullResult pull(File repoFolder, CredentialsProvider cp) {
}
}
+ /**
+ * Computes the differences between the working directory and the index of the repository.
+ *
+ * @param repoFolder the folder of the local repository
+ * @return a list of differences
+ */
public static List diff(File repoFolder) {
try {
SpecsLogs.msgInfo("Diff repo in folder '" + repoFolder + "'");
@@ -274,12 +282,28 @@ public static List diff(File repoFolder) {
}
}
+ /**
+ * Clones a repository into the specified folder.
+ *
+ * @param repositoryPath the URL of the repository
+ * @param outputFolder the folder where the repository will be cloned
+ * @param cp the credentials provider
+ * @return the Git object representing the cloned repository
+ */
public static Git clone(String repositoryPath, File outputFolder, CredentialsProvider cp) {
return clone(repositoryPath, outputFolder, cp, null);
}
+ /**
+ * Clones a repository into the specified folder and checks out the specified branch.
+ *
+ * @param repositoryPath the URL of the repository
+ * @param outputFolder the folder where the repository will be cloned
+ * @param cp the credentials provider
+ * @param branch the branch to check out
+ * @return the Git object representing the cloned repository
+ */
public static Git clone(String repositoryPath, File outputFolder, CredentialsProvider cp, String branch) {
- // TODO: tag should be branch
String repoName = getRepoName(repositoryPath);
// Get repo folder
@@ -311,15 +335,36 @@ public static Git clone(String repositoryPath, File outputFolder, CredentialsPro
}
}
+ /**
+ * Normalizes a tag name by adding the "refs/tags/" prefix if it is not already present.
+ *
+ * @param tag the tag name
+ * @return the normalized tag name
+ */
public static String normalizeTag(String tag) {
var prefix = "refs/tags/";
return tag.startsWith(prefix) ? tag : prefix + tag;
}
+ /**
+ * Checks if the specified tag exists in the remote repository.
+ *
+ * @param repositoryPath the URL of the repository
+ * @param tag the tag name
+ * @return true if the tag exists, false otherwise
+ */
public static boolean hasTag(String repositoryPath, String tag) {
return hasTag(repositoryPath, tag, null);
}
+ /**
+ * Checks if the specified tag exists in the remote repository.
+ *
+ * @param repositoryPath the URL of the repository
+ * @param tag the tag name
+ * @param credentialsProvider the credentials provider
+ * @return true if the tag exists, false otherwise
+ */
public static boolean hasTag(String repositoryPath, String tag, CredentialsProvider credentialsProvider) {
LsRemoteCommand ls = Git.lsRemoteRepository();
try {
@@ -334,17 +379,18 @@ public static boolean hasTag(String repositoryPath, String tag, CredentialsProvi
.setTags(true) // include tags in result
.callAsMap();
- // System.out.println("TAGS: " + remoteRefs.keySet());
- //
- // var prefix = "refs/tags/";
- // var completeTag = tag.startsWith(prefix) ? tag : prefix + tag;
-
return remoteRefs.keySet().contains(normalizeTag(tag));
} catch (Exception e) {
throw new RuntimeException("Could not check tags of repository '" + repositoryPath + "'", e);
}
}
+ /**
+ * Extracts the name of the repository from its URL.
+ *
+ * @param repositoryPath the URL of the repository
+ * @return the name of the repository
+ */
public static String getRepoName(String repositoryPath) {
try {
String repoPath = new URI(repositoryPath).getPath();
@@ -369,10 +415,24 @@ public static String getRepoName(String repositoryPath) {
}
+ /**
+ * Returns the folder where repositories are stored.
+ *
+ * @return the folder where repositories are stored
+ */
public static File getRepositoriesFolder() {
return new File(SpecsIo.getTempFolder(), SPECS_GIT_REPOS_FOLDER);
}
+ /**
+ * Clones or pulls a repository into the specified folder.
+ *
+ * @param repositoryPath the URL of the repository
+ * @param outputFolder the folder where the repository will be cloned or pulled
+ * @param user the username for authentication
+ * @param password the password for authentication
+ * @return the folder where the repository is located
+ */
public static File cloneOrPull(String repositoryPath, File outputFolder, String user, String password) {
String repoName = getRepoName(repositoryPath);
@@ -384,25 +444,18 @@ public static File cloneOrPull(String repositoryPath, File outputFolder, String
try {
SpecsLogs.msgInfo("Cloning repo '" + repositoryPath + "' to folder '" + repoFolder + "'");
- // Delete folder to avoid errors
- // if (SpecsIo.isEmptyFolder(repoFolder)) {
- // SpecsIo.deleteFolder(repoFolder);
- // }
var git = Git.cloneRepository()
.setURI(repositoryPath)
.setDirectory(repoFolder);
if (user != null & password != null) {
CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user, password);
- git.setCredentialsProvider(cp);// .call();
+ git.setCredentialsProvider(cp);
}
- // System.out.println("OUTPUT FOLDER:" + outputFolder);
Git repo = git.call();
- // File workTree = git.getRepository().getWorkTree();
repo.close();
- // System.out.println("REPO: " + workTree);
return repoFolder;
} catch (GitAPIException e) {
throw new RuntimeException("Could not clone repository '" + repositoryPath + "'", e);
@@ -428,6 +481,13 @@ public static File cloneOrPull(String repositoryPath, File outputFolder, String
return repoFolder;
}
+ /**
+ * Adds the specified files to the index of the repository.
+ *
+ * @param repoFolder the folder of the local repository
+ * @param filesToAdd the files to add
+ * @return the result of the add operation
+ */
public static DirCache add(File repoFolder, List filesToAdd) {
System.out.println("Git add to " + repoFolder);
try {
@@ -449,6 +509,12 @@ public static DirCache add(File repoFolder, List filesToAdd) {
}
}
+ /**
+ * Commits and pushes the changes in the repository.
+ *
+ * @param repoFolder the folder of the local repository
+ * @param cp the credentials provider
+ */
public static void commitAndPush(File repoFolder, CredentialsProvider cp) {
System.out.println("Commiting and pushing " + repoFolder);
@@ -461,21 +527,18 @@ public static void commitAndPush(File repoFolder, CredentialsProvider cp) {
}
+ /**
+ * Checks if the specified commit value is the name of a branch in the repository.
+ *
+ * @param repo the Git repository
+ * @param commit the commit value
+ * @return true if the commit value is the name of a branch, false otherwise
+ */
public static boolean isBranchName(Git repo, String commit) {
- // Check if commit value is the name of a branch
- // Taken from here: https://stackoverflow.com/a/57365145
-
try {
-
- // for (var branchRef : repo.branchList().setListMode(ListMode.REMOTE).call()) {
- // System.out.println("BRANCH: " + branchRef.getName());
- // }
-
- // Pattern to search for
var commitPattern = "refs/remotes/origin/" + commit;
return repo.branchList()
- // So that it returns all remotes
.setListMode(ListMode.REMOTE)
.call()
.stream()
diff --git a/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java b/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java
index 3e0b9d71..3bdc725f 100644
--- a/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java
+++ b/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java
@@ -1,14 +1,14 @@
/**
* Copyright 2024 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.git;
@@ -19,14 +19,23 @@
import org.junit.Test;
+/**
+ * Unit tests for the SpecsGit utility class.
+ */
public class GitTester {
+ /**
+ * Tests the presence of a specific tag in a remote Git repository.
+ */
@Test
public void testTag() {
assertTrue(SpecsGit.hasTag("https://github.com/specs-feup/clava.git", "clang_ast_dumper_v4.2.18"));
assertTrue(!SpecsGit.hasTag("https://github.com/specs-feup/clava.git", "ddddd"));
}
+ /**
+ * Tests cloning a remote Git repository to a local folder.
+ */
@Test
public void testClone() {
SpecsGit.clone("https://github.com/specs-feup/clava.git", new File("C:\\Temp\\clone_test"), null,
diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java b/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java
index cec44cb3..2c820818 100644
--- a/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java
+++ b/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java
@@ -1,14 +1,14 @@
/**
* Copyright 2018 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gprofer;
@@ -42,172 +42,182 @@
import pt.up.fe.specs.util.system.ProcessOutputAsString;
import pt.up.fe.specs.util.utilities.LineStream;
+/**
+ * Utility class for profiling binaries using gprof and parsing the results.
+ */
public class Gprofer {
+ /**
+ * Parses a gprof text profile from the given file path.
+ *
+ * @param textProfilePath path to the gprof text profile
+ * @return a GprofData object containing the parsed data
+ */
public static GprofData parseTextProfile(String textProfilePath) {
-
File textProfile = new File(textProfilePath);
return parseTextProfile(textProfile);
}
+ /**
+ * Parses a gprof text profile from the given file.
+ *
+ * @param textProfile file containing the gprof text profile
+ * @return a GprofData object containing the parsed data
+ */
private static GprofData parseTextProfile(File textProfile) {
-
return parseGprof(SpecsIo.toInputStream(textProfile));
}
/**
- * Runs on a temporary directory.
- *
- * @param binaryPath
- * @return
+ * Profiles the given binary using gprof in a temporary directory.
+ *
+ * @param binaryPath path to the binary to profile
+ * @return a GprofData object containing the profiling results
*/
public static GprofData profile(String binaryPath) {
-
return profile(new File(binaryPath));
}
/**
- * Runs on a temporary directory.
- *
- * @param binary
- * @return
+ * Profiles the given binary using gprof in a temporary directory.
+ *
+ * @param binary the binary file to profile
+ * @return a GprofData object containing the profiling results
*/
public static GprofData profile(File binary) {
-
return profile(binary, Collections.emptyList(), 1);
}
/**
- * Runs on a temporary directory.
- *
- * @param binary
- * @param args
- * @param numRuns
- * @return
+ * Profiles the given binary using gprof in a temporary directory, with arguments and number of runs.
+ *
+ * @param binary the binary file to profile
+ * @param args arguments to pass to the binary
+ * @param numRuns number of times to run the binary
+ * @return a GprofData object containing the profiling results
*/
public static GprofData profile(File binary, List args, int numRuns) {
-
File workingDir = SpecsIo.mkdir(
SpecsIo.getTempFolder(),
"gprofer_" + UUID.randomUUID().toString());
-
boolean deleteWorkingDir = true;
boolean checkReturn = true;
-
return profile(binary, args, numRuns, workingDir, deleteWorkingDir, checkReturn);
}
/**
- * Runs on then provided directory.
- *
- * @param binary
- * @param args
- * @param numRuns
- * @param workingDir
- * @param deleteWorkingDir
- * @param checkReturn
- * TODO
- * @return
+ * Profiles the given binary using gprof in the provided directory.
+ *
+ * @param binary the binary file to profile
+ * @param args arguments to pass to the binary
+ * @param numRuns number of times to run the binary
+ * @param workingDir the working directory to use
+ * @param deleteWorkingDir whether to delete the working directory after profiling
+ * @param checkReturn whether to check the return code of the process
+ * @return a GprofData object containing the profiling results
*/
public static GprofData profile(File binary, List args, int numRuns, File workingDir,
boolean deleteWorkingDir, boolean checkReturn) {
-
if (!binary.exists()) {
throw new RuntimeException("Could not locate the binary \"" + binary + "\".");
}
-
if (!workingDir.exists()) {
throw new RuntimeException("Could not locate the working directory \"" + workingDir + "\".");
}
-
int currentRun = 0;
-
List gmons = new ArrayList<>();
-
List filesToDelete = new ArrayList<>();
if (deleteWorkingDir) {
filesToDelete.add(workingDir);
}
-
while (currentRun < numRuns) {
-
runBinary(binary, args, workingDir, checkReturn);
-
makeGmon(currentRun, workingDir, filesToDelete, gmons);
-
currentRun++;
}
-
GprofData data = summarizeGmons(binary, workingDir, gmons, filesToDelete);
-
deleteTempFiles(filesToDelete);
-
return data;
}
+ /**
+ * Summarizes the gmon files into a single GprofData object.
+ *
+ * @param binary the binary file
+ * @param workingDir the working directory
+ * @param gmons the list of gmon files
+ * @param filesToDelete files to delete after processing
+ * @return a GprofData object containing the summarized data
+ */
private static GprofData summarizeGmons(File binary, File workingDir,
List gmons, List filesToDelete) {
-
List command = new ArrayList<>();
command.add("gprof");
command.add("-bp");
command.add("-zc");
command.add("-s");
command.add(binary.getAbsolutePath());
-
List gmonNames = gmons.stream().map(File::getAbsolutePath).collect(Collectors.toList());
command.addAll(gmonNames);
-
ProcessOutput result = SpecsSystem.runProcess(
command,
workingDir,
Gprofer::parseGprof, Function.identity());
-
File gmonSum = new File(workingDir, "gmon.sum");
filesToDelete.add(gmonSum);
-
return result.getStdOut();
}
+ /**
+ * Moves the gmon.out file to a new file for the current run.
+ *
+ * @param currentRun the current run index
+ * @param workingDir the working directory
+ * @param filesToDelete files to delete after processing
+ * @param gmons list to add the new gmon file to
+ */
private static void makeGmon(int currentRun, File workingDir, List filesToDelete, List gmons) {
-
File gmon = new File(workingDir, "gmon.out");
File newGmon = new File(gmon.getAbsolutePath() + "." + currentRun);
gmons.add(newGmon);
-
try {
Files.move(gmon.toPath(), newGmon.toPath(), StandardCopyOption.ATOMIC_MOVE);
} catch (Exception e) {
throw new RuntimeException("Could not move file '" + gmon + "'", e);
}
-
filesToDelete.add(newGmon);
}
+ /**
+ * Runs the binary with the given arguments in the specified working directory.
+ *
+ * @param binary the binary file
+ * @param args arguments to pass to the binary
+ * @param workingDir the working directory
+ * @param checkReturn whether to check the return code of the process
+ */
private static void runBinary(File binary, List args, File workingDir, boolean checkReturn) {
-
List binaryCommand = new ArrayList<>();
binaryCommand.add(binary.getAbsolutePath());
binaryCommand.addAll(args);
-
ProcessOutputAsString result = SpecsSystem.runProcess(binaryCommand, workingDir, true, false);
-
if (checkReturn && result.isError()) {
-
SpecsLogs.setPrintStackTrace(false);
SpecsLogs.warn("Could not profile the binary \"" + binary + "\". Execution terminated with error.");
SpecsLogs.warn("stdout: " + result.getStdOut());
SpecsLogs.warn("stderr: " + result.getStdErr());
SpecsLogs.setPrintStackTrace(true);
-
throw new RuntimeException();
}
}
+ /**
+ * Deletes the temporary files and folders used during profiling.
+ *
+ * @param filesToDelete list of files and folders to delete
+ */
private static void deleteTempFiles(List filesToDelete) {
-
for (File file : filesToDelete) {
-
if (file.isDirectory()) {
SpecsIo.deleteFolder(file);
} else {
@@ -216,40 +226,61 @@ private static void deleteTempFiles(List filesToDelete) {
}
}
+ /**
+ * Converts the given GprofData object to its JSON representation.
+ *
+ * @param data the GprofData object
+ * @return a JSON string representing the data
+ */
public static String getJsonData(GprofData data) {
-
return new Gson().toJson(data);
}
+ /**
+ * Parses gprof output from a string.
+ *
+ * @param gprofOutput the gprof output as a string
+ * @return a GprofData object containing the parsed data
+ */
private static GprofData parseGprof(String gprofOutput) {
-
InputStream gprofStream = new ByteArrayInputStream(gprofOutput.getBytes(Charset.defaultCharset()));
-
return parseGprof(gprofStream);
}
+ /**
+ * Parses gprof output from an InputStream.
+ *
+ * @param gprofStream the gprof output as an InputStream
+ * @return a GprofData object containing the parsed data
+ */
private static GprofData parseGprof(InputStream gprofStream) {
-
Map table = parseTable(gprofStream);
List hotspots = makeHotspots(table);
-
return new GprofData(table, hotspots);
}
+ /**
+ * Creates a list of hotspots sorted by percentage from the profiling table.
+ *
+ * @param table the profiling table
+ * @return a list of function names sorted by percentage
+ */
private static List makeHotspots(Map table) {
-
List hotspots = table.values().stream()
.sorted(Comparator.comparing(GprofLine::getPercentage).reversed())
.map(row -> row.getName())
.collect(Collectors.toList());
-
return hotspots;
}
+ /**
+ * Parses the profiling table from the gprof output InputStream.
+ *
+ * @param gprofOutput the gprof output InputStream
+ * @return a map of function names to GprofLine objects
+ */
private static Map parseTable(InputStream gprofOutput) {
-
LineStream lines = LineStream.newInstance(gprofOutput, "gprof output");
-
return lines.stream()
.map(Gprofer::parseLine)
.filter(Optional::isPresent)
@@ -257,36 +288,32 @@ private static Map parseTable(InputStream gprofOutput) {
.collect(Collectors.toMap(GprofLine::getName, Function.identity()));
}
+ /**
+ * Parses a single line of gprof output into a GprofLine object.
+ *
+ * @param line the line of gprof output
+ * @return an Optional containing the GprofLine if parsing was successful, or empty otherwise
+ */
private static Optional parseLine(String line) {
-
StringSplitter splitter = new StringSplitter(line.trim());
-
Optional percentageTry = splitter.parseTry(StringSplitterRules::doubleNumber);
if (percentageTry.isPresent()) {
-
Double percentage = percentageTry.get();
Double cumulativeSeconds = splitter.parse(StringSplitterRules::doubleNumber);
Double selfSeconds = splitter.parse(StringSplitterRules::doubleNumber);
-
Integer calls = null;
Double selfMsCall = null;
Double totalMsCall = null;
-
Optional callsTry = splitter.parseTry(StringSplitterRules::integer);
if (callsTry.isPresent()) {
-
calls = callsTry.get();
selfMsCall = splitter.parse(StringSplitterRules::doubleNumber);
totalMsCall = splitter.parse(StringSplitterRules::doubleNumber);
}
-
- // String name = splitter.parse(StringSplitterRules::string);
String name = splitter.toString();
name = name.replaceAll(", ", ",");
-
GprofLine gproferRow = new GprofLine(percentage, cumulativeSeconds, selfSeconds, calls, selfMsCall,
totalMsCall, name);
-
return Optional.ofNullable(gproferRow);
}
return Optional.empty();
diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java b/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java
index 13446c2c..a0ed29b9 100644
--- a/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java
+++ b/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java
@@ -1,14 +1,14 @@
/**
* Copyright 2018 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gprofer;
@@ -19,40 +19,47 @@
import pt.up.fe.specs.gprofer.data.GprofData;
+/**
+ * Entry point for running gprofer profiling from the command line.
+ */
public class GproferMain {
+ /**
+ * Main method for running a profiling session and printing the results as JSON.
+ *
+ * @param args command line arguments (not used in this example)
+ */
public static void main(String[] args) {
-
File binary = new File(
"/home/pedro/Documents/repositories/AntarexIT4I-master/Betweenness/Code/build/betweenness");
List binaryArgs = Arrays.asList("-f",
"/home/pedro/Documents/repositories/AntarexIT4I-master/Betweenness/Graphs/graph-prt-port.csv");
-
int numRuns = 1;
-
- // File workingDir = new File("/home/pedro/Desktop/gprof_tests/src/");
- // boolean deleteWorkingDir = false;
- // boolean checkReturn = true;
-
GprofData data = Gprofer.profile(binary,
binaryArgs,
numRuns);
-
System.out.println(Gprofer.getJsonData(data));
-
- // warn("This is a warning message");
}
+ /**
+ * Prints a warning message with stack trace information.
+ *
+ * @param message the warning message
+ */
static void warn(String message) {
-
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement s = stackTrace[2]; // 0 is getStracktrace, 1 is this method
-
System.out.printf(buildShortMessage(message, s));
}
+ /**
+ * Builds a short message with class, method, file, and line number information.
+ *
+ * @param message the message
+ * @param s the stack trace element
+ * @return the formatted message
+ */
static String buildShortMessage(String message, StackTraceElement s) {
-
StringBuilder builder = new StringBuilder(message);
builder.append(" ");
builder.append(s.getClassName());
@@ -63,7 +70,6 @@ static String buildShortMessage(String message, StackTraceElement s) {
builder.append(":");
builder.append(s.getLineNumber());
builder.append(")");
-
return builder.toString();
}
diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java
index 2d63b3f5..ddacdd4d 100644
--- a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java
+++ b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java
@@ -1,14 +1,14 @@
/**
* Copyright 2018 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gprofer.data;
@@ -16,20 +16,39 @@
import java.util.List;
import java.util.Map;
+/**
+ * Represents the parsed data from a gprof profiling session, including the table of profiling lines and the list of hotspots.
+ */
public class GprofData {
private final Map table;
private final List hotspots;
+ /**
+ * Constructs a GprofData object with the given table and hotspots.
+ *
+ * @param table a map of function names to their profiling data
+ * @param hotspots a list of function names sorted by their profiling percentage
+ */
public GprofData(Map table, List hotspots) {
this.table = table;
this.hotspots = hotspots;
}
+ /**
+ * Returns the table of profiling data.
+ *
+ * @return a map of function names to GprofLine objects
+ */
public Map getTable() {
return table;
}
+ /**
+ * Returns the list of hotspots (function names sorted by percentage).
+ *
+ * @return a list of function names
+ */
public List getHotspots() {
return hotspots;
}
diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java
index 86d1b35f..e2331add 100644
--- a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java
+++ b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java
@@ -1,18 +1,21 @@
/**
* Copyright 2018 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package pt.up.fe.specs.gprofer.data;
+/**
+ * Represents a single line of profiling data from gprof output.
+ */
public class GprofLine {
private final Double percentage;
@@ -23,6 +26,17 @@ public class GprofLine {
private final Double totalMsCall;
private final String name;
+ /**
+ * Constructs a GprofLine with the given profiling values.
+ *
+ * @param percentage the percentage of time spent in this function
+ * @param cumulativeSeconds the cumulative seconds up to this function
+ * @param selfSeconds the self seconds spent in this function
+ * @param calls the number of calls to this function
+ * @param selfMsCall the self milliseconds per call
+ * @param totalMsCall the total milliseconds per call
+ * @param name the function name
+ */
public GprofLine(Double percentage, Double cumulativeSeconds, Double selfSeconds, Integer calls, Double selfMsCall,
Double totalMsCall, String name) {
this.percentage = percentage;
@@ -34,30 +48,51 @@ public GprofLine(Double percentage, Double cumulativeSeconds, Double selfSeconds
this.name = name;
}
+ /**
+ * @return the percentage of time spent in this function
+ */
public Double getPercentage() {
return percentage;
}
+ /**
+ * @return the cumulative seconds up to this function
+ */
public Double getCumulativeSeconds() {
return cumulativeSeconds;
}
+ /**
+ * @return the self seconds spent in this function
+ */
public Double getSelfSeconds() {
return selfSeconds;
}
+ /**
+ * @return the number of calls to this function
+ */
public Integer getCalls() {
return calls;
}
+ /**
+ * @return the self milliseconds per call
+ */
public Double getSelfMsCall() {
return selfMsCall;
}
+ /**
+ * @return the total milliseconds per call
+ */
public Double getTotalMsCall() {
return totalMsCall;
}
+ /**
+ * @return the function name
+ */
public String getName() {
return name;
}
diff --git a/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java b/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java
index 77cadac5..78077a02 100644
--- a/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java
+++ b/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java
@@ -1,14 +1,14 @@
/**
* Copyright 2012 SPeCS Research Group.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package org.suikasoft.GsonPlus;
@@ -18,38 +18,47 @@
import pt.up.fe.specs.util.utilities.PersistenceFormat;
/**
- * @author Joao Bispo
- *
+ * Implementation of {@link PersistenceFormat} for JSON serialization using Gson.
*/
public class JsonPersistence extends PersistenceFormat {
private final Gson gson;
/**
- *
+ * Constructs a new JsonPersistence instance with a default Gson object.
*/
public JsonPersistence() {
gson = new Gson();
}
- /* (non-Javadoc)
- * @see org.specs.DymaLib.Graphs.Utils.PersistenceFormat.PersistenceFormat#to(java.lang.Object, java.lang.Object[])
+ /**
+ * Serializes the given object to a JSON string.
+ *
+ * @param anObject the object to serialize
+ * @return the JSON string
*/
@Override
public String to(Object anObject) {
return gson.toJson(anObject);
}
- /* (non-Javadoc)
- * @see org.specs.DymaLib.Graphs.Utils.PersistenceFormat.PersistenceFormat#from(java.lang.String, java.lang.Class, java.lang.Object[])
+ /**
+ * Deserializes the given JSON string to an object of the specified class.
+ *
+ * @param contents the JSON string
+ * @param classOfObject the class to deserialize to
+ * @param the type of the object
+ * @return the deserialized object
*/
@Override
public T from(String contents, Class classOfObject) {
return gson.fromJson(contents, classOfObject);
}
- /* (non-Javadoc)
- * @see pt.up.fe.specs.util.Utilities.PersistenceFormat#getExtension()
+ /**
+ * Returns the file extension for JSON files.
+ *
+ * @return the string "json"
*/
@Override
public String getExtension() {
diff --git a/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java b/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java
index e53b1b5c..b1b4ccd9 100644
--- a/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java
+++ b/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java
@@ -1,14 +1,14 @@
/**
* Copyright 2019 SPeCS.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License. under the License.
+ * specific language governing permissions and limitations under the License.
*/
package org.suikasoft.GsonPlus;
@@ -23,42 +23,76 @@
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
+/**
+ * Utility class for working with Gson serialization and deserialization.
+ */
public class SpecsGson {
+ /**
+ * Serializes an object to a pretty-printed JSON string.
+ *
+ * @param object the object to serialize
+ * @return the JSON string
+ */
public static String toJson(Object object) {
return new GsonBuilder().setPrettyPrinting().create().toJson(object);
}
+ /**
+ * Deserializes a JSON string to an object of the given class.
+ *
+ * @param json the JSON string
+ * @param aClass the class to deserialize to
+ * @param the type of the object
+ * @return the deserialized object
+ */
public static T fromJson(String json, Class aClass) {
return new Gson().fromJson(json, aClass);
}
+ /**
+ * Deserializes a JSON string to a map.
+ *
+ * @param json the JSON string
+ * @return the deserialized map
+ */
@SuppressWarnings("unchecked")
public static Map