diff --git a/.devops/gctoolkit-release.yml b/.devops/gctoolkit-release.yml index 49b10dba..f29bada0 100644 --- a/.devops/gctoolkit-release.yml +++ b/.devops/gctoolkit-release.yml @@ -167,7 +167,7 @@ extends: displayName: 'Remove .sha1 and .md5 files' workingDirectory: $(Build.ArtifactStagingDirectory) - # ESRP Sign all jars in the semantickernel-java directory + # ESRP Sign all jars in the gctoolkit staging directory - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: ESRP Java JAR Signing inputs: @@ -206,7 +206,7 @@ extends: MaxRetryAttempts: '5' VerboseLogin: true - # gpg sign all artifacts in the semantickernel-java directory + # gpg sign all artifacts in the gctoolkit staging directory # this will create a .asc file for each file in the directory. This is a detached signature # required to publish into Maven Central. - bash: | diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..204c1c2a --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,100 @@ +# GitHub Copilot Instructions for GCToolKit + +## Project Overview +GCToolKit is a Java-based toolkit for analyzing Garbage Collection logs from various JVM implementations. The project uses Maven as its build system and is structured as a multi-module project. + +## Architecture & Design Patterns +- **Multi-module Maven project** with clear separation of concerns +- **Event-driven architecture** using Vert.x for message passing +- **Parser framework** for processing various GC log formats +- **Channel-based communication** between parsers and event consumers +- **Verticle pattern** for asynchronous processing with Vert.x + +## Module Structure +- `api/` - Core API definitions, interfaces, and base classes +- `parser/` - GC log parsing implementations for different JVM types +- `vertx/` - Vert.x-based messaging and event handling infrastructure +- `sample/` - Example usage and sample applications +- `IT/` - Integration tests +- `gclogs/` - Test data and sample GC logs + +## Key Technologies +- **Java 21+** with module system (module-info.java files) +- **Vert.x 5.0.2** for reactive programming and event bus +- **JUnit 5** for testing +- **Maven** for build management +- **Spotbugs, PMD, Checkstyle** for code quality + +## Code Style & Conventions +- Follow standard Java naming conventions +- Use proper JavaDoc for public APIs +- Implement equals/hashCode when appropriate +- Use logging with java.util.logging +- Handle exceptions appropriately with try-catch blocks +- Use Promise/Future patterns for asynchronous operations with Vert.x + +## Common Patterns in This Codebase + +### Event Processing +```java +public void start(Promise promise) { + vertx.eventBus().consumer(inbox, message -> { + processor.receive(message.body()); + }).completion() + .onComplete(ar -> promise.complete()); +} +``` + +### Channel Implementation +- Extend appropriate base channel classes +- Implement proper lifecycle management (open/close) +- Use Vert.x deployVerticle for async operations +- Handle deployment IDs for proper cleanup + +### Error Handling +- Use java.util.logging.Logger for logging +- Log warnings/errors with proper context +- Handle Future completion with onComplete callbacks + +## Vert.x Specific Guidelines +- Use Verticles for isolated processing units +- Deploy verticles asynchronously with completion handlers +- Use the event bus for inter-verticle communication +- Properly manage deployment IDs to avoid double-undeploy issues +- Handle Promise completion in start() methods + +## Testing Conventions +- Integration tests go in the `IT/` module +- Unit tests use JUnit 5 +- Test GC log files are stored in `gclogs/` with organized subdirectories +- Mock external dependencies appropriately + +## Build & Dependencies +- Maven 3.9.11+ required +- Use the Maven Wrapper (i.e., `mvnw`) +- Keep dependencies up to date, unless you see a comment saying why not to +- Use dependencyManagement in parent POM for version consistency +- Include proper test scopes for test dependencies + +## When Suggesting Code Changes +1. Consider the event-driven nature of the architecture +2. Ensure proper async handling with Vert.x patterns +3. Maintain compatibility with existing interfaces +4. Add appropriate logging and error handling +5. Follow the established module boundaries +6. Consider performance implications for GC log processing +7. Ensure proper resource cleanup (channels, verticles, etc.) + +## Common Issues to Avoid +- Double-undeployment of Vert.x verticles +- Blocking operations in Vert.x event loops +- Memory leaks from unclosed channels or resources +- Incorrect Promise/Future completion handling +- Missing error handling in async operations + +## File Naming Patterns +- Verticles: `*Verticle.java` +- Channels: `*Channel.java` +- Events: `*Event.java` +- Tests: `*Test.java` +- Integration tests: `*IT.java` or in IT module diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1dcba990..d0f0aeeb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,7 +39,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/early_access.yml b/.github/workflows/early_access.yml index 8301d3ec..1a1b35eb 100644 --- a/.github/workflows/early_access.yml +++ b/.github/workflows/early_access.yml @@ -12,7 +12,7 @@ jobs: if: startsWith(github.event.head_commit.message, '[maven-release-plugin]') != true steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 - name: Set up JDK 11 uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 diff --git a/.github/workflows/jreleaser.yml b/.github/workflows/jreleaser.yml index 4ff93d63..be9f8081 100644 --- a/.github/workflows/jreleaser.yml +++ b/.github/workflows/jreleaser.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout project - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: main fetch-depth: 0 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 3c49d36c..a14541ab 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,7 +21,7 @@ jobs: java: [11, 17, 21] steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 67adb6ef..f6f8db62 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout project - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: main fetch-depth: 0 diff --git a/.github/workflows/release_to_github.yml b/.github/workflows/release_to_github.yml index 36cabbfa..f7496e59 100644 --- a/.github/workflows/release_to_github.yml +++ b/.github/workflows/release_to_github.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout project - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: ref: main fetch-depth: 0 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index d58ddb0c..efa17b70 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,5 +14,5 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar diff --git a/pmd-ruleset.xml b/pmd-ruleset.xml index ab72e122..9b20eae9 100644 --- a/pmd-ruleset.xml +++ b/pmd-ruleset.xml @@ -6,8 +6,8 @@ --> + xmlns="http://pmd.sourceforge.net/ruleset/3.0.0" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/3.0.0 https://pmd.sourceforge.io/ruleset_3_0_0.xsd"> PMD rules customised for gctoolkit code. @@ -17,20 +17,34 @@ + + + + + + + + + + + + + + @@ -52,7 +66,9 @@ + + @@ -61,16 +77,20 @@ + + + + @@ -88,11 +108,14 @@ + + + @@ -104,6 +127,7 @@ + @@ -115,19 +139,30 @@ + + + + + + + + + + + @@ -141,14 +176,17 @@ + + + @@ -168,11 +206,11 @@ - + diff --git a/pom.xml b/pom.xml index 34d07c64..7fddcb03 100644 --- a/pom.xml +++ b/pom.xml @@ -45,41 +45,39 @@ 3.0.3-SNAPSHOT - 10.23.0 - 1.17.0 - 5.12.2 + 10.26.1 + 1.19.0 + 5.13.4 3.1.0 - 3.0.0-M2 + 3.0.0-M3 3.6.0 - 3.4.1 + 3.5.0 3.14.0 11 3.8.1 3.1.4 1.0 - 3.5.0 - 3.5.0 + 3.6.1 + 3.5.1 3.1.4 0.8.13 3.4.2 3.11.2 3.6.0 - 2.5.0 - - 3.21.2 + 2.6.0 + 3.28.0 3.9.0 0.16.1 3.1.1 3.3.1 4.0.0-M16 3.3.1 - 4.9.3.0 + 4.9.3.2 3.5.3 - 3.9.9 + 3.9.11 2.18.0 0.9.1 - - 6.55.0 + 7.18.0 UTF-8 microsoft/gctoolkit git@github.com:${project.github.repository}.git @@ -254,6 +252,12 @@ org.apache.maven.plugins maven-pmd-plugin ${maven.pmd-plugin.version} + + ${maven.compiler.release} + + pmd-ruleset.xml + + net.sourceforge.pmd @@ -385,10 +389,7 @@ org.apache.maven.plugins maven-changes-plugin - - https - 443 false false @@ -396,8 +397,6 @@ changes - - diff --git a/vertx/pom.xml b/vertx/pom.xml index e4603603..be7bb3ef 100644 --- a/vertx/pom.xml +++ b/vertx/pom.xml @@ -22,7 +22,7 @@ io.vertx vertx-core - 4.5.14 + 5.0.2 org.junit.jupiter diff --git a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/DataSourceVerticle.java b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/DataSourceVerticle.java index 59c6b323..354ea153 100644 --- a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/DataSourceVerticle.java +++ b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/DataSourceVerticle.java @@ -57,10 +57,9 @@ public void start(Promise promise) { try { vertx.eventBus().consumer(inbox, message -> { processor.receive(message.body()); - if (GCLogFile.END_OF_DATA_SENTINEL.equals(message.body())) { - vertx.undeploy(id); - } - }).completionHandler(result -> {promise.complete();}); + // Removed vertx.undeploy(id) to avoid double-undeploy + }).completion() + .onComplete(ar -> promise.complete()); } catch(Throwable t) { LOGGER.log(Level.WARNING,"Vertx: processing DataSource failed",t); } diff --git a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/JVMEventVerticle.java b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/JVMEventVerticle.java index 68cd585b..227eab51 100644 --- a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/JVMEventVerticle.java +++ b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/JVMEventVerticle.java @@ -59,13 +59,11 @@ public void start(Promise promise) { try { processor.receive(event); } catch (Throwable t) { - // Throwable is caught because we don't want the processor to blow up the message bus. LOGGER.log(Level.WARNING, "Vertx: processing JVMEvent failed", t); } - if (event instanceof JVMTermination) { - vertx.undeploy(id); - } - }).completionHandler(result -> {promise.complete();}); + // Removed vertx.undeploy(id) to avoid double-undeploy + }).completion() + .onComplete(ar -> promise.complete()); } @Override diff --git a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxChannel.java b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxChannel.java index 3de6b3fa..f91e821b 100644 --- a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxChannel.java +++ b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxChannel.java @@ -47,12 +47,13 @@ protected Vertx vertx() { * Closes the Vert.x instance. */ public void close() { - vertx().close(result -> { - if (result.succeeded()) { - LOGGER.log(Level.FINE, "Vertx: closed"); - } else { - LOGGER.log(Level.FINE, "Vertx: close failed", result.cause()); - } - }); + vertx().close() + .onComplete(ar -> { + if (ar.succeeded()) { + LOGGER.log(Level.FINE, "Vertx: closed"); + } else { + LOGGER.log(Level.WARNING, "Vertx: close failed", ar.cause()); + } + }); } } \ No newline at end of file diff --git a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxDataSourceChannel.java b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxDataSourceChannel.java index f873448f..47ccf54a 100644 --- a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxDataSourceChannel.java +++ b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxDataSourceChannel.java @@ -29,10 +29,11 @@ public VertxDataSourceChannel() { public void registerListener(DataSourceParser listener) { final DataSourceVerticle processor = new DataSourceVerticle(vertx(), listener.channel().getName(), listener); CountDownLatch latch = new CountDownLatch(1); - vertx().deployVerticle(processor, state -> { - processor.setID((state.succeeded()) ? state.result() : ""); - latch.countDown(); - }); + vertx().deployVerticle(processor) + .onComplete(ar -> { + processor.setID(ar.succeeded() ? ar.result() : ""); + latch.countDown(); + }); try { latch.await(); } catch (InterruptedException e) { diff --git a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxJVMEventChannel.java b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxJVMEventChannel.java index 6737ae9d..77eb0bdc 100644 --- a/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxJVMEventChannel.java +++ b/vertx/src/main/java/com/microsoft/gctoolkit/vertx/VertxJVMEventChannel.java @@ -35,10 +35,11 @@ public VertxJVMEventChannel() {} public void registerListener(JVMEventChannelListener listener) { final JVMEventVerticle processor = new JVMEventVerticle(vertx(), listener.channel().getName(), listener); CountDownLatch latch = new CountDownLatch(1); - vertx().deployVerticle(processor, state -> { - processor.setID((state.succeeded()) ? state.result() : ""); - latch.countDown(); - }); + vertx().deployVerticle(processor) + .onComplete(ar -> { + processor.setID(ar.succeeded() ? ar.result() : ""); + latch.countDown(); + }); try { latch.await();