Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
contents: read
strategy:
matrix:
java: ['17', '21', '23']
java: ['17', '21', '24']
env:
DEFAULT_JAVA: '17'
runs-on: ubuntu-latest
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:

- name: Sonar analysis
if: ${{ env.DEFAULT_JAVA == matrix.java && env.SONAR_TOKEN != null }}
run: ./gradlew sonar --info --exclude-task integrationTest -Dsonar.token=$SONAR_TOKEN
run: ./gradlew sonar --info -Dsonar.token=$SONAR_TOKEN
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Build Javadoc
run: ./gradlew javadoc --info
- name: Build Reports
run: ./gradlew check jacocoTestReport --exclude-task integrationTest --info
run: ./gradlew check jacocoTestReport --info
- name: Collect artifacts
run: cp -r build/reports/ build/docs/
- name: Upload artifact
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ jobs:
if: ${{ !inputs.skip-deploy-maven-central }}
run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository --warning-mode all
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.OSSRH_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.OSSRH_PASSWORD }}
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.MAVEN_CENTRAL_PORTAL_TOKEN }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.MAVEN_CENTRAL_PORTAL_USERNAME }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}

Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@
"-Djava.util.logging.config.file=src/test/resources/logging.properties"
]
},
"sonarlint.connectedMode.project": {
"connectionId": "itsallcode",
"projectKey": "org.itsallcode:simple-process"
}
}
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] - unreleased

## [0.1.0] - 2025-02-??

* [PR #1](https://github.com/itsallcode/simple-process/pull/1): Initial release
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,94 @@ Wrapper to simplify working with external processes.

**This project is at an early development stage and the API will change without backwards compatibility.**

[![Java CI](https://github.com/itsallcode/simple-process/actions/workflows/build.yml/badge.svg)](https://github.com/itsallcode/simple-process/actions/workflows/build.yml)
[![CodeQL](https://github.com/itsallcode/simple-process/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/itsallcode/simple-process/actions/workflows/codeql-analysis.yml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Asimple-process&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Asimple-process)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Asimple-process&metric=coverage)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Asimple-process)
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Asimple-process&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Asimple-process)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Asimple-process&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Asimple-process)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Asimple-process&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Asimple-process)
[![Maven Central](https://img.shields.io/maven-central/v/org.itsallcode/simple-process)](https://search.maven.org/artifact/org.itsallcode/simple-process)

* [Changelog](CHANGELOG.md)
* [API JavaDoc](https://blog.itsallcode.org/simple-process/javadoc/org.itsallcode.process/module-summary.html)
* [Test report](https://blog.itsallcode.org/simple-process/reports/tests/test/index.html)
* [Coverage report](https://blog.itsallcode.org/simple-process/reports/jacoco/test/html/index.html)

## Usage

This project requires Java 17 or later.

### Add Dependency

Add dependency to your Gradle project:

```groovy
dependencies {
implementation 'org.itsallcode:simple-process:0.1.0'
}
```

Add dependency to your Maven project:

```xml
<dependency>
<groupId>org.itsallcode</groupId>
<artifactId>simple-process</artifactId>
<version>0.1.0</version>
</dependency>
```

### Features

Simplified API for starting external processes and executable JARs. Allows easy capturing stdout and stderr and forwarding to log output.

## Development

### Check if dependencies are up-to-date

```sh
./gradlew dependencyUpdates
```

### Building

Install to local maven repository:

```sh
./gradlew publishToMavenLocal
```

### Test Coverage

To calculate and view test coverage:

```sh
./gradlew check jacocoTestReport
open build/reports/jacoco/test/html/index.html
```

### View Generated Javadoc

```sh
./gradlew javadoc
open build/docs/javadoc/index.html
```

### Publish to Maven Central

#### Preparations

1. Checkout the `main` branch, create a new branch.
2. Update version number in `build.gradle` and `README.md`.
3. Add changes in new version to `CHANGELOG.md`.
4. Commit and push changes.
5. Create a new pull request, have it reviewed and merged to `main`.

#### Perform the Release

1. Start the release workflow
* Run command `gh workflow run release.yml --repo itsallcode/simple-process --ref main`
* or go to [GitHub Actions](https://github.com/itsallcode/simple-process/actions/workflows/release.yml) and start the `release.yml` workflow on branch `main`.
2. Update title and description of the newly created [GitHub release](https://github.com/itsallcode/simple-process/releases).
3. After some time the release will be available at [Maven Central](https://repo1.maven.org/maven2/org/itsallcode/simple-process/).
18 changes: 12 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
id 'jacoco-report-aggregation'
id 'signing'
id 'maven-publish'
id 'org.sonarqube' version '6.0.1.5171'
id 'org.sonarqube' version '6.2.0.5505'
id "io.github.gradle-nexus.publish-plugin" version "2.0.0"
id 'com.github.ben-manes.versions' version '0.52.0'
}
Expand All @@ -18,17 +18,21 @@ repositories {
}

dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api libs.commons.math3

// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation libs.guava
}

testing {
suites {
test {
useJUnitJupiter(libs.versions.junitJupiter.get())
dependencies {
implementation libs.assertj
implementation libs.mockitoJunit
}
targets.all {
testTask.configure {
systemProperty 'java.util.logging.config.file', file('src/test/resources/logging.properties')
}
}
}
}
}
Expand Down Expand Up @@ -119,6 +123,8 @@ nexusPublishing {
repositories {
sonatype {
stagingProfileId = "546ea6ce74787e"
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format

[versions]
commons-math3 = "3.6.1"
guava = "33.3.1-jre"
junitJupiter = "5.11.1"
mockito = "5.18.0"
assertj = "3.27.3"
equalsverifier = "3.18.1"
tostringverifier = "1.4.8"

[libraries]
commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }

mockitoJunit = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
equalsverifier = { module = "nl.jqno.equalsverifier:equalsverifier", version.ref = "equalsverifier" }
tostringverifier = { module = "com.jparams:to-string-verifier", version.ref = "tostringverifier" }
assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
6 changes: 3 additions & 3 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

/**
* Simplified usage of Java's {@link java.lang.Process} API.
* <p>
* Create a new process builder using
* {@link org.itsallcode.process.SimpleProcessBuilder#create()} and start the
* process with
* {@link org.itsallcode.process.SimpleProcessBuilder#start()}.
*/

module simple.process {
exports org.itsallcode.process;

requires java.logging;
}
18 changes: 0 additions & 18 deletions src/main/java/org/example/Library.java

This file was deleted.

42 changes: 42 additions & 0 deletions src/main/java/org/itsallcode/process/AsyncStreamConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.itsallcode.process;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import java.util.logging.Logger;

class AsyncStreamConsumer implements Runnable {
private static final Logger LOG = Logger.getLogger(AsyncStreamConsumer.class.getName());
private final String name;
private final long pid;
private final ProcessStreamConsumer consumer;
private final InputStream stream;

AsyncStreamConsumer(final String name, final long pid, final InputStream stream,
final ProcessStreamConsumer consumer) {
this.name = name;
this.pid = pid;
this.stream = stream;
this.consumer = consumer;
}

@Override
public void run() {
LOG.finest(() -> "Start reading from '%s' stream of process %d...".formatted(name, pid));
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
String line = null;
while ((line = reader.readLine()) != null) {
consumer.accept(line);
}
LOG.finest(() -> "Stream '%s' of process %d finished".formatted(name, pid));
consumer.streamFinished();
} catch (final IOException exception) {
final Level logLevel = "Stream closed".equals(exception.getMessage()) ? Level.FINEST : Level.WARNING;
LOG.log(logLevel,
"Reading stream '%s' of process %d failed: %s".formatted(name, pid,
exception.getMessage()),
exception);
consumer.streamReadingFailed(exception);
}
}
}
29 changes: 29 additions & 0 deletions src/main/java/org/itsallcode/process/DelegatingConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.itsallcode.process;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

class DelegatingConsumer implements ProcessStreamConsumer {

private final List<ProcessStreamConsumer> delegates;

DelegatingConsumer(final List<ProcessStreamConsumer> delegates) {
this.delegates = Collections.unmodifiableList(delegates);
}

@Override
public void accept(final String line) {
delegates.forEach(delegate -> delegate.accept(line));
}

@Override
public void streamFinished() {
delegates.forEach(ProcessStreamConsumer::streamFinished);
}

@Override
public void streamReadingFailed(final IOException exception) {
delegates.forEach(delegate -> delegate.streamReadingFailed(exception));
}
}
Loading