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: 2 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
steps:

- name: Check out sources
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Set up JDK 21
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
if: github.repository_owner == 'spring-projects'
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: docs-build
fetch-depth: 1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
ref: ${{ matrix.branch }}

- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 17
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/milestone.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
run: ./mvnw -B clean deploy -Pci,artifactory

- name: Setup Graphviz
uses: ts-graphviz/setup-graphviz@v1
uses: ts-graphviz/setup-graphviz@v2

- name: Deploy documentation
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
./mvnw -B clean deploy -Pci,sonatype -s settings.xml

- name: Setup Graphviz
uses: ts-graphviz/setup-graphviz@v1
uses: ts-graphviz/setup-graphviz@v2

- name: Deploy documentation
env:
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
<flapdoodle-mongodb.version>4.20.0</flapdoodle-mongodb.version>
<jgit.version>7.6.0.202603022253-r</jgit.version>
<jmolecules-bom.version>2025.0.2</jmolecules-bom.version>
<jobrunr.version>8.5.2</jobrunr.version>
<jobrunr.version>8.6.0</jobrunr.version>
<json-unit-assertj.version>5.1.1</json-unit-assertj.version>
<namastack-outbox.version>1.4.0</namastack-outbox.version>
<namastack-outbox.version>1.6.0</namastack-outbox.version>
<nullaway.version>0.13.0</nullaway.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
import io.toolisticon.cute.CuteApi.DoCustomAssertions;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

import org.junit.jupiter.api.Test;
import org.springframework.boot.json.BasicJsonParser;

import com.jayway.jsonpath.JsonPath;

Expand Down Expand Up @@ -71,6 +74,27 @@ void extractsJavadocFromMethodWithClassNestedInInterfaceAsParameter() {
});
}

@Test // GH-1685
void javadocWithDoubleQuotesProducesValidJson() throws Exception {

assertSucceded(getSourceFile("SampleComponentWithQuotes"));

var content = Files.readString(new File(JSON_LOCATION).toPath(), StandardCharsets.UTF_8);

assertThatNoException()
.as("BasicJsonParser must be able to parse the generated JSON without errors")
.isThrownBy(() -> new BasicJsonParser().parseList(content));

var context = JsonPath.parse(new File(JSON_LOCATION));
var comment = context.read(
"$[?(@.name == 'example.SampleComponentWithQuotes')].methods[0].comment",
String[].class)[0];

assertThat(comment)
.as("Javadoc comment must preserve literal double-quote characters")
.contains("\"foo\"", "\"bar\"");
}

@Test // GH-1430
void extractsPackageComments() throws Exception {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package example;

/**
* Component whose method Javadoc contains literal double-quote characters.
*/
class SampleComponentWithQuotes {

/**
* Finds something by key. Example:
*
* <pre>
* findByKey("foo", "bar")
* </pre>
*/
void findByKey(String a, String b) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ public ApplicationModules verify(VerificationOptions options) {
}

/**
* Executes all verifications to be applied and returns {@link Violations} if any occured. Will always execute the
* Executes all verifications to be applied and returns {@link Violations} if any occurred. Will always execute the
* verifications in contrast to {@link #verify()} which just runs once.
*
* @return will never be {@literal null}.
Expand All @@ -470,7 +470,7 @@ public Violations detectViolations() {

/**
* Executes all verifications to be applied considering the given {@link VerificationOptions} and returns
* {@link Violations} if any occured. Will always execute the verifications in contrast to {@link #verify()} which
* {@link Violations} if any occurred. Will always execute the verifications in contrast to {@link #verify()} which
* just runs once.
*
* @return will never be {@literal null}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.springframework.modulith.events.support.BrokerRouting;
import org.springframework.modulith.events.support.EventExternalizationTransport;
import org.springframework.modulith.events.support.EventExternalizerModuleListener;
import org.springframework.modulith.events.support.OutboxEventExternalizer;
import org.springframework.modulith.events.support.OutboxEventExternalizerFactory;

/**
Expand Down Expand Up @@ -73,26 +74,24 @@ EventExternalizerModuleListener rabbitEventExternalizer(EventExternalizationConf
@ConditionalOnProperty(name = ExternalizationMode.PROPERTY, havingValue = "outbox")
static class RabbitOutboxConfiguration {

private final EventExternalizationTransport transport;
private final OutboxEventExternalizer externalizer;

RabbitOutboxConfiguration(EventExternalizationConfiguration configuration, RabbitMessageOperations operations,
BeanFactory beanFactory) {
BeanFactory beanFactory, OutboxEventExternalizerFactory factory) {

this.transport = createRabbitTransport(configuration, operations, beanFactory);
this.externalizer = factory.forTransport(createRabbitTransport(configuration, operations, beanFactory));
}

@AutoConfiguration
@ConditionalOnClass(OutboxHandler.class)
class NamastackOutboxAutoConfiguration {

@Bean
OutboxHandler kafkaOutboxExternalizer(OutboxEventExternalizerFactory factory) {
OutboxHandler kafkaOutboxExternalizer() {

logger.debug("Registering Namastack domain event outbox externalization to RabbitMQ.");

var externalizer = factory.forTransport(transport);

return (payload, metadata) -> externalizer.externalize(payload);
return (payload, metadata) -> externalizer.externalizeBlocking(payload);
}
}

Expand All @@ -101,13 +100,11 @@ OutboxHandler kafkaOutboxExternalizer(OutboxEventExternalizerFactory factory) {
class JobRunrOutboxAutoConfiguration {

@Bean
JobRunrExternalizationTransport jobRunrOutboxExternalizer(OutboxEventExternalizerFactory factory) {
JobRunrExternalizationTransport jobRunrOutboxExternalizer() {

logger.debug("Registering JobRunr domain event outbox externalization to RabbitMQ.");

var externalizer = factory.forTransport(transport);

return payload -> externalizer.externalize(payload);
return payload -> externalizer.externalizeBlocking(payload);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.springframework.modulith.events.support;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.modulith.events.EventExternalizationConfiguration;
Expand Down Expand Up @@ -65,4 +66,24 @@ public CompletableFuture<?> externalize(Object event) {
return it;
});
}

/**
* Externalizes the given event in a blocking way.
*
* @param event must not be {@literal null}.
* @see #externalize(Object)
*/
public void externalizeBlocking(Object event) {

try {

externalize(event).get();

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ OutboxHandler namastackJmsOutboxExternalizer() {

logger.debug("Registering Namastack domain event outbox externalization to JMS.");

return (payload, metadata) -> externalizer.externalize(payload);
return (payload, metadata) -> externalizer.externalizeBlocking(payload);
}
}

Expand All @@ -104,7 +104,7 @@ JobRunrExternalizationTransport jobRunrJmsOutboxExternalizer() {

logger.debug("Registering JobRunr domain event outbox externalization to JMS.");

return externalizer::externalize;
return externalizer::externalizeBlocking;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ public List<TargetEventPublication> findFailedPublications(FailedCriteria criter
public void deletePublications(List<UUID> identifiers) {

batch(identifiers, DELETE_BATCH_SIZE).forEach(it -> {
entityManager.createQuery(DELETE).setParameter(1, identifiers).executeUpdate();
entityManager.createQuery(DELETE).setParameter(1, it).executeUpdate();
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2026 the original author or authors.
*
* 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
*
* https://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.
*/
package org.springframework.modulith.events.jpa;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.junit.jupiter.api.Test;
import org.springframework.modulith.events.core.EventSerializer;
import org.springframework.modulith.events.support.CompletionMode;

/**
* Unit tests for {@link JpaEventPublicationRepository}.
*
* @author Oliver Drotbohm
*/
class JpaEventPublicationRepositoryUnitTests {

@Test
void deletesPublicationsByIdentifierInBatches() {

var entityManager = mock(EntityManager.class);
var query = mock(Query.class);
var parameters = new ArrayList<List<UUID>>();

when(entityManager.createQuery(anyString())).thenReturn(query);
when(query.setParameter(eq(1), any())).thenAnswer(invocation -> {

parameters.add(invocation.getArgument(1));
return query;
});

var repository = new JpaEventPublicationRepository(entityManager, mock(EventSerializer.class),
CompletionMode.UPDATE);
var identifiers = new ArrayList<UUID>();

for (var i = 0; i < 101; i++) {
identifiers.add(UUID.randomUUID());
}

repository.deletePublications(identifiers);

verify(query, times(2)).executeUpdate();

assertThat(parameters).extracting(List::size).containsExactly(100, 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ static class KafkaOutboxConfiguration {
class NamastackOutboxAutoConfiguration {

@Bean
OutboxHandler namastackKafkaOutboxExternalizer(OutboxEventExternalizerFactory factory) {
OutboxHandler namastackKafkaOutboxExternalizer() {

logger.debug("Registering Namastack domain event outbox externalization to Kafka.");

return (payload, metadata) -> externalizer.externalize(payload);
return (payload, metadata) -> externalizer.externalizeBlocking(payload);
}
}

Expand All @@ -103,11 +103,11 @@ OutboxHandler namastackKafkaOutboxExternalizer(OutboxEventExternalizerFactory fa
class JobRunrOutboxAutoConfiguration {

@Bean
JobRunrExternalizationTransport jobRunrKafkaOutboxExternalizer(OutboxEventExternalizerFactory factory) {
JobRunrExternalizationTransport jobRunrKafkaOutboxExternalizer() {

logger.debug("Registering JobRunr domain event outbox externalization to Kafka.");

return payload -> externalizer.externalize(payload);
return payload -> externalizer.externalizeBlocking(payload);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ OutboxHandler namastackKafkaOutboxExternalizer() {

logger.debug("Registering Namastack domain event outbox externalization for Spring Messaging.");

return (payload, metadata) -> externalizer.externalize(payload);
return (payload, metadata) -> externalizer.externalizeBlocking(payload);
}
}

Expand All @@ -104,7 +104,7 @@ JobRunrExternalizationTransport jobRunrKafkaOutboxExternalizer() {

logger.debug("Registering JobRunr domain event outbox externalization for Spring Messaging.");

return externalizer::externalize;
return externalizer::externalizeBlocking;
}
}
}
Expand Down
Loading