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
11 changes: 6 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run (skip actual publish)'
description: "Dry run (skip actual publish)"
type: boolean
default: true

Expand Down Expand Up @@ -36,10 +36,11 @@ jobs:
if: ${{ steps.release.outputs.pr != '' }}
run: |
VERSION=$(cat version.txt)
# Update Maven example
# Update Maven examples (all version tags)
sed -i "s|<version>[0-9]*\.[0-9]*\.[0-9]*</version>|<version>${VERSION}</version>|g" README.md
# Update Gradle example
# Update Gradle examples (both starters)
sed -i "s|judoscale-spring-boot-starter:[0-9]*\.[0-9]*\.[0-9]*|judoscale-spring-boot-starter:${VERSION}|g" README.md
sed -i "s|judoscale-spring-boot-2-starter:[0-9]*\.[0-9]*\.[0-9]*|judoscale-spring-boot-2-starter:${VERSION}|g" README.md

- name: Commit version updates
if: ${{ steps.release.outputs.pr != '' }}
Expand All @@ -63,8 +64,8 @@ jobs:
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
java-version: "21"
distribution: "temurin"

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
Expand Down
36 changes: 35 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ Judoscale automatically scales your application based on request queue time and

**Supported Frameworks**

- **Spring Boot** — `judoscale-spring-boot-starter`
- **Spring Boot 3.x** — `judoscale-spring-boot-starter`
- **Spring Boot 2.x** — `judoscale-spring-boot-2-starter`

## judoscale-spring-boot-starter

For Spring Boot 3.x applications.

### Requirements

- Java 21 or later
Expand Down Expand Up @@ -40,6 +43,37 @@ Add the dependency to your `build.gradle`:
implementation 'com.judoscale:judoscale-spring-boot-starter:0.1.2'
```

## judoscale-spring-boot-2-starter

For Spring Boot 2.x applications (legacy support).

### Requirements

- Java 8 or later
- Spring Boot 2.6 or later (2.x series)

### Installation

#### Maven

Add the dependency to your `pom.xml`:

```xml
<dependency>
<groupId>com.judoscale</groupId>
<artifactId>judoscale-spring-boot-2-starter</artifactId>
<version>0.1.2</version>
</dependency>
```

#### Gradle

Add the dependency to your `build.gradle`:

```groovy
implementation 'com.judoscale:judoscale-spring-boot-2-starter:0.1.2'
```

### Usage

#### Getting Started
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ subprojects {
group = "com.judoscale"
version = projectVersion

// Default to Java 21, but allow subprojects to override
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
Expand Down
13 changes: 12 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
[versions]
spring-boot = "3.2.2"
spring-boot2 = "2.6.7"
jackson = "2.16.1"
slf4j = "2.0.11"
junit = "5.10.1"
assertj = "3.24.2"
mockito = "5.8.0"
byte-buddy = "1.14.11"
httpclient = "4.5.14"

[libraries]
# Spring Boot
# Spring Boot 3.x
spring-boot-autoconfigure = { module = "org.springframework.boot:spring-boot-autoconfigure", version.ref = "spring-boot" }
spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" }
spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "spring-boot" }
spring-boot-configuration-processor = { module = "org.springframework.boot:spring-boot-configuration-processor", version.ref = "spring-boot" }

# Spring Boot 2.x
spring-boot2-autoconfigure = { module = "org.springframework.boot:spring-boot-autoconfigure", version.ref = "spring-boot2" }
spring-boot2-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot2" }
spring-boot2-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "spring-boot2" }
spring-boot2-configuration-processor = { module = "org.springframework.boot:spring-boot-configuration-processor", version.ref = "spring-boot2" }

# HTTP Client (for Java 8 compatibility)
httpclient = { module = "org.apache.httpcomponents:httpclient", version.ref = "httpclient" }

# Jackson
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }

Expand Down
6 changes: 6 additions & 0 deletions judoscale-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ plugins {

description = "Core library for Judoscale Java integrations"

// judoscale-core targets Java 8 for maximum compatibility
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
// Testing
testImplementation(libs.junit.jupiter)
Expand Down
66 changes: 60 additions & 6 deletions judoscale-core/src/main/java/com/judoscale/core/Metric.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
package com.judoscale.core;

import java.time.Instant;
import java.util.Objects;

/**
* Represents a single metric measurement.
* Metrics: qt = queue time, at = application time, nt = network time, up = utilization percentage
*/
public record Metric(
String identifier,
long value,
Instant time,
String queueName
) {
public final class Metric {

private final String identifier;
private final long value;
private final Instant time;
private final String queueName;

/**
* Creates a metric with all fields.
*/
public Metric(String identifier, long value, Instant time, String queueName) {
this.identifier = identifier;
this.value = value;
this.time = time;
this.queueName = queueName;
}

/**
* Creates a web request metric (no queue name).
*/
Expand All @@ -25,4 +37,46 @@ public Metric(String identifier, long value, Instant time) {
public Metric(String identifier, long value) {
this(identifier, value, Instant.now(), null);
}

public String identifier() {
return identifier;
}

public long value() {
return value;
}

public Instant time() {
return time;
}

public String queueName() {
return queueName;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Metric metric = (Metric) o;
return value == metric.value &&
Objects.equals(identifier, metric.identifier) &&
Objects.equals(time, metric.time) &&
Objects.equals(queueName, metric.queueName);
}

@Override
public int hashCode() {
return Objects.hash(identifier, value, time, queueName);
}

@Override
public String toString() {
return "Metric{" +
"identifier='" + identifier + '\'' +
", value=" + value +
", time=" + time +
", queueName='" + queueName + '\'' +
'}';
}
}
1 change: 1 addition & 0 deletions judoscale-spring-boot-2-starter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
99 changes: 99 additions & 0 deletions judoscale-spring-boot-2-starter/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import com.vanniktech.maven.publish.SonatypeHost

plugins {
alias(libs.plugins.maven.publish)
}

description = "Autoscaling for Spring Boot 2.x applications on Heroku, AWS, and other cloud hosts"

// Spring Boot 2 starter targets Java 8
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

// Generate version properties file during build
tasks.register("generateVersionProperties") {
val outputDir = layout.buildDirectory.dir("generated/resources")
val versionValue = version.toString()

outputs.dir(outputDir)

doLast {
val propsDir = outputDir.get().asFile.resolve("META-INF")
propsDir.mkdirs()
propsDir.resolve("judoscale.properties").writeText("version=$versionValue\n")
}
}

tasks.named<ProcessResources>("processResources") {
dependsOn("generateVersionProperties")
from(layout.buildDirectory.dir("generated/resources"))
}

dependencies {
// Judoscale Core
api(project(":judoscale-core"))

// Spring Boot 2.x Web (provided - the app will have this)
compileOnly(libs.spring.boot2.starter.web)
testImplementation(libs.spring.boot2.starter.web)

// Spring Boot 2.x Auto-configuration
implementation(libs.spring.boot2.autoconfigure)

// For @ConfigurationProperties
annotationProcessor(libs.spring.boot2.configuration.processor)

// JSON processing (for API client)
implementation(libs.jackson.databind)

// HTTP client for Java 8 (Apache HttpClient)
implementation(libs.httpclient)

// Logging
implementation(libs.slf4j.api)

// Testing
testImplementation(libs.spring.boot2.starter.test)
testImplementation(libs.junit.jupiter)
testImplementation(libs.assertj.core)
testImplementation(libs.mockito.core)
testImplementation(libs.mockito.junit.jupiter)
}

mavenPublishing {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)
signAllPublications()

coordinates(group.toString(), "judoscale-spring-boot-2-starter", version.toString())

pom {
name.set("Judoscale Spring Boot 2 Starter")
description.set(project.description)
inceptionYear.set("2024")
url.set("https://github.com/judoscale/judoscale-java")

licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
distribution.set("https://opensource.org/licenses/MIT")
}
}

developers {
developer {
id.set("judoscale")
name.set("Judoscale")
email.set("support@judoscale.com")
}
}

scm {
connection.set("scm:git:git://github.com/judoscale/judoscale-java.git")
developerConnection.set("scm:git:ssh://github.com/judoscale/judoscale-java.git")
url.set("https://github.com/judoscale/judoscale-java")
}
}
}
Loading
Loading