Skip to content
Open
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: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ hs_err_pid*
# macOS
.DS_Store

.vscode/

# IntelliJ
.idea/
*.iml
Expand All @@ -25,4 +27,4 @@ out/
**/build/

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
!gradle-wrapper.jar
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.11.0
1.12.0
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,49 @@

### Requirements

Java 8 or above and `javax.servlet` version 3.
Java 8 or above and `javax.servlet` version 3, **or** `jakarta.servlet` version 5.0 or above for Jakarta EE environments.

> If you are using Spring, we recommend leveraging Spring's OIDC and OAuth2 support, as demonstrated by the [Spring Boot Quickstart](https://auth0.com/docs/quickstart/webapp/java-spring-boot).

### Installation

#### For traditional Java EE / javax.servlet environments:

Add the dependency via Maven:

```xml
<dependency>
<groupId>com.auth0</groupId>
<artifactId>mvc-auth-commons</artifactId>
<version>1.12.0</version>
</dependency>
```

or Gradle:

```gradle
implementation 'com.auth0:mvc-auth-commons:1.12.0'
```

#### For Jakarta EE / jakarta.servlet environments:

Starting from version 1.12.0, this library provides dual support for both javax.servlet and jakarta.servlet environments through bytecode transformation. For Jakarta EE environments (Tomcat 10+ etc.), use the Jakarta-compatible version:

Add the dependency via Maven:

```xml
<dependency>
<groupId>com.auth0</groupId>
<artifactId>mvc-auth-commons</artifactId>
<version>1.11.0</version>
<version>1.12.0</version>
<classifier>jakarta</classifier>
</dependency>
```

or Gradle:

```gradle
implementation 'com.auth0:mvc-auth-commons:1.11.0'
implementation 'com.auth0:mvc-auth-commons:1.12.0:jakarta'
```

### Configure Auth0
Expand Down
158 changes: 155 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ version = getVersionFromFile()
group = GROUP
logger.lifecycle("Using version ${version} for ${name} group $group")

import me.champeau.gradle.japicmp.JapicmpTask

project.afterEvaluate {
def versions = project.ext.testInJavaVersions
for (pluginJavaTestVersion in versions) {
Expand All @@ -49,7 +47,7 @@ project.afterEvaluate {

project.configure(project) {
def baselineVersion = project.ext.baselineCompareVersion
task('apiDiff', type: JapicmpTask, dependsOn: 'jar') {
task('apiDiff', type: me.champeau.gradle.japicmp.JapicmpTask, dependsOn: 'jar') {
oldClasspath.from(files(getBaselineJar(project, baselineVersion)))
newClasspath.from(files(jar.archiveFile))
onlyModified = true
Expand Down Expand Up @@ -106,6 +104,20 @@ java {
}
}

// Jakarta test source set configuration
sourceSets {
testJakarta {
java {
srcDirs = ['src/testJakarta/java']
}
resources {
srcDirs = ['src/testJakarta/resources']
}
compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}

compileJava {
sourceCompatibility '1.8'
targetCompatibility '1.8'
Expand Down Expand Up @@ -136,6 +148,146 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
testImplementation 'org.springframework:spring-test:4.3.14.RELEASE'
testImplementation 'com.squareup.okhttp3:okhttp:4.11.0'

// Jakarta test dependencies (Java 8 compatible)
testJakartaImplementation 'jakarta.servlet:jakarta.servlet-api:5.0.0'
testJakartaImplementation 'org.apache.commons:commons-lang3:3.12.0'
testJakartaImplementation 'com.google.guava:guava-annotations:r03'
testJakartaImplementation 'commons-codec:commons-codec:1.15'
testJakartaImplementation 'com.auth0:auth0:1.45.1'
testJakartaImplementation 'com.auth0:java-jwt:3.19.4'
testJakartaImplementation 'com.auth0:jwks-rsa:0.22.1'
testJakartaImplementation 'org.bouncycastle:bcprov-jdk15on:1.64'
testJakartaImplementation 'org.hamcrest:java-hamcrest:2.0.0.0'
testJakartaImplementation 'org.hamcrest:hamcrest-core:1.3'
testJakartaImplementation 'org.mockito:mockito-core:2.8.9'
testJakartaImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
testJakartaImplementation 'org.springframework:spring-test:4.3.14.RELEASE'
testJakartaImplementation 'com.squareup.okhttp3:okhttp:4.11.0'
testJakartaRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}

// Task to perform javax to jakarta transformation using Eclipse Transformer
tasks.register('performJakartaTransformation') {
dependsOn tasks.named('classes')

def transformedClassesDir = file("$buildDir/transformed-classes")

outputs.dir transformedClassesDir
inputs.files sourceSets.main.output.classesDirs

doLast {
// Eclipse Transformer CLI configuration
def transformerVersion = '0.5.0'
def transformerConfig = project.configurations.detachedConfiguration(
project.dependencies.create("org.eclipse.transformer:org.eclipse.transformer.cli:${transformerVersion}")
)

// Clean and create output directory
delete transformedClassesDir
transformedClassesDir.mkdirs()

// Transform each classes directory directly to the root of transformedClassesDir
sourceSets.main.output.classesDirs.each { classDir ->
if (classDir.exists()) {
project.javaexec {
classpath = transformerConfig
main = 'org.eclipse.transformer.cli.JakartaTransformerCLI'
args = [
classDir.absolutePath,
transformedClassesDir.absolutePath,
'-q' // quiet mode
]
}
}
}

// Also transform resources if needed
sourceSets.main.output.resourcesDir.with { resourceDir ->
if (resourceDir.exists()) {
copy {
from resourceDir
into transformedClassesDir
}
}
}

println "Jakarta transformation completed: ${transformedClassesDir.absolutePath}"
}
}

// Task to create Jakarta JAR from transformed classes
tasks.register('transformJarToJakarta', Jar) {
dependsOn tasks.named('performJakartaTransformation')

archiveClassifier = 'jakarta'

def transformedClassesDir = file("$buildDir/transformed-classes")

from transformedClassesDir

// Include original manifest with modifications
manifest {
from(tasks.jar.manifest) {
attributes.remove('Implementation-Title')
attributes.remove('Implementation-Version')
}
attributes(
'Implementation-Title': "${project.name}-jakarta",
'Implementation-Version': project.version,
'Jakarta-Transformed': 'true'
)
}

doLast {
println "Jakarta JAR file generated: ${archiveFile.get().asFile.absolutePath}"
}
}

// Jakarta test task (updated to use transformed JAR)
tasks.register('testJakarta', Test) {
description = "Runs Jakarta EE specific tests using transformed jakarta classes"
group = "verification"
testClassesDirs = sourceSets.testJakarta.output.classesDirs

// Build classpath with Jakarta dependencies and transformed JAR
def jakartaClasspath = sourceSets.testJakarta.runtimeClasspath

shouldRunAfter tasks.named('test')
dependsOn tasks.named('transformJarToJakarta')

// Enabled for Jakarta testing
enabled = true

useJUnitPlatform()
testLogging {
events "skipped", "failed"
exceptionFormat "short"
}

doFirst {
def jakartaJar = tasks.transformJarToJakarta.archiveFile.get().asFile
if (jakartaJar.exists()) {
// Replace main classes with jakarta-transformed JAR in classpath
classpath = jakartaClasspath + files(jakartaJar) - sourceSets.main.output
} else {
throw new GradleException("Jakarta JAR not found: ${jakartaJar.absolutePath}")
}
}
}

// Enable Jakarta tests in check task
tasks.named('check').configure {
dependsOn tasks.named('testJakarta')
}

// Ensure Jakarta JAR is built when running build task
tasks.named('build').configure {
dependsOn tasks.named('transformJarToJakarta')
}

apply from: rootProject.file('gradle/maven-publish.gradle')




19 changes: 19 additions & 0 deletions gradle/maven-publish.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,18 @@ publishing {
artifactId = POM_ARTIFACT_ID
version = getVersionName()

// 1. Main artifact (javax version) - no classifier
artifact("$buildDir/libs/${project.name}-${version}.jar")

// 2. Jakarta EE version artifact - with 'jakarta' classifier
artifact("$buildDir/libs/${project.name}-${version}-jakarta.jar") {
classifier 'jakarta'
}

// 3. Sources JAR
artifact sourcesJar

// 4. Javadoc JAR
artifact javadocJar

pom {
Expand Down Expand Up @@ -94,6 +104,15 @@ publishing {
}
}

// Ensure Jakarta JAR is built before publishing
tasks.named('publishMavenJavaPublicationToSonatypeRepository').configure {
dependsOn tasks.named('transformJarToJakarta')
}

tasks.named('publishToMavenLocal').configure {
dependsOn tasks.named('transformJarToJakarta')
}

signing {
def signingKey = System.getenv("SIGNING_KEY")
def signingPassword = System.getenv("SIGNING_PASSWORD")
Expand Down
85 changes: 85 additions & 0 deletions src/testJakarta/java/com/auth0/JakartaCompatibilityTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.auth0;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.Cookie;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Tests to verify Jakarta EE compatibility.
* These tests ensure Jakarta servlet API is available and working.
*/
public class JakartaCompatibilityTest {

@Test
public void testJakartaServletRequestAvailable() {
// Verify Jakarta HttpServletRequest can be mocked and used
HttpServletRequest mockRequest = Mockito.mock(HttpServletRequest.class);
assertNotNull(mockRequest, "Jakarta HttpServletRequest should be available");

// Verify it's from Jakarta package
String className = mockRequest.getClass().getName();
assertTrue(className.contains("jakarta") || className.contains("HttpServletRequest"),
"Should be Jakarta servlet type");
}

@Test
public void testJakartaServletResponseAvailable() {
// Verify Jakarta HttpServletResponse can be mocked and used
HttpServletResponse mockResponse = Mockito.mock(HttpServletResponse.class);
assertNotNull(mockResponse, "Jakarta HttpServletResponse should be available");
}

@Test
public void testJakartaHttpSessionAvailable() {
// Test Jakarta HttpSession
HttpSession mockSession = Mockito.mock(HttpSession.class);
assertNotNull(mockSession, "Jakarta HttpSession should be available");

// Test basic session operations
Mockito.when(mockSession.getAttribute("test")).thenReturn("value");
Mockito.doNothing().when(mockSession).setAttribute("test", "value");

mockSession.setAttribute("test", "value");
Mockito.verify(mockSession).setAttribute("test", "value");
}

@Test
public void testJakartaCookieAvailable() {
// Test Jakarta Cookie
Cookie cookie = new Cookie("testCookie", "testValue");
assertNotNull(cookie, "Jakarta Cookie should be available");

cookie.setPath("/");
cookie.setSecure(true);
cookie.setHttpOnly(true);

// Verify basic cookie properties
assertNotNull(cookie.getName());
assertNotNull(cookie.getValue());
}

@Test
public void testJakartaServletIntegration() {
// Test integration between Jakarta servlet components
HttpServletRequest mockRequest = Mockito.mock(HttpServletRequest.class);
HttpServletResponse mockResponse = Mockito.mock(HttpServletResponse.class);
HttpSession mockSession = Mockito.mock(HttpSession.class);

// Setup mock behavior
Mockito.when(mockRequest.getSession(true)).thenReturn(mockSession);
Mockito.when(mockRequest.getContextPath()).thenReturn("/test");

// Test session retrieval
HttpSession session = mockRequest.getSession(true);
assertNotNull(session, "Session should be retrieved successfully");

String contextPath = mockRequest.getContextPath();
assertNotNull(contextPath, "Context path should be available");
}
}
Loading