diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java index 25393756fe4f1d..25a685b6ff228c 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java @@ -20,6 +20,7 @@ import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; +import java.util.logging.LogManager; import java.util.logging.LogRecord; import org.graalvm.nativeimage.ImageInfo; @@ -102,22 +103,26 @@ public void accept(String loggerName, CleanupFilterConfig config) { } }); } + LogCleanupFilter cleanupFiler = new LogCleanupFilter(filterElements); + for (Handler handler : LogManager.getLogManager().getLogger("").getHandlers()) { + handler.setFilter(cleanupFiler); + } final ArrayList handlers = new ArrayList<>(3 + additionalHandlers.size()); if (config.console.enable) { - final Handler consoleHandler = configureConsoleHandler(config.console, consoleConfig, errorManager, filterElements, + final Handler consoleHandler = configureConsoleHandler(config.console, consoleConfig, errorManager, cleanupFiler, possibleFormatters, possibleBannerSupplier, launchMode); errorManager = consoleHandler.getErrorManager(); handlers.add(consoleHandler); } if (config.file.enable) { - handlers.add(configureFileHandler(config.file, errorManager, filterElements)); + handlers.add(configureFileHandler(config.file, errorManager, cleanupFiler)); } if (config.syslog.enable) { - final Handler syslogHandler = configureSyslogHandler(config.syslog, errorManager, filterElements); + final Handler syslogHandler = configureSyslogHandler(config.syslog, errorManager, cleanupFiler); if (syslogHandler != null) { handlers.add(syslogHandler); } @@ -125,7 +130,7 @@ public void accept(String loggerName, CleanupFilterConfig config) { if (!categories.isEmpty()) { Map namedHandlers = createNamedHandlers(config, consoleConfig, possibleFormatters, errorManager, - filterElements, launchMode); + cleanupFiler, launchMode); Map additionalNamedHandlersMap; if (additionalNamedHandlers.isEmpty()) { @@ -166,7 +171,7 @@ public void accept(String categoryName, CategoryConfig config) { if (optional.isPresent()) { final Handler handler = optional.get(); handler.setErrorManager(errorManager); - handler.setFilter(new LogCleanupFilter(filterElements)); + handler.setFilter(cleanupFiler); handlers.add(handler); } } @@ -191,18 +196,20 @@ public static void initializeBuildTimeLogging(LogConfig config, LogBuildTimeConf filterElements.add( new LogCleanupFilterElement(entry.getKey(), entry.getValue().targetLevel, entry.getValue().ifStartsWith)); } + LogCleanupFilter logCleanupFilter = new LogCleanupFilter(filterElements); final ArrayList handlers = new ArrayList<>(3); if (config.console.enable) { - final Handler consoleHandler = configureConsoleHandler(config.console, consoleConfig, errorManager, filterElements, + final Handler consoleHandler = configureConsoleHandler(config.console, consoleConfig, errorManager, + logCleanupFilter, Collections.emptyList(), new RuntimeValue<>(Optional.empty()), launchMode); errorManager = consoleHandler.getErrorManager(); handlers.add(consoleHandler); } Map namedHandlers = createNamedHandlers(config, consoleConfig, Collections.emptyList(), errorManager, - filterElements, launchMode); + logCleanupFilter, launchMode); for (Map.Entry entry : categories.entrySet()) { final CategoryBuildTimeConfig buildCategory = isSubsetOf(entry.getKey(), buildConfig.categories); @@ -262,7 +269,7 @@ private static CategoryBuildTimeConfig isSubsetOf(String categoryName, Map createNamedHandlers(LogConfig config, ConsoleRuntimeConfig consoleRuntimeConfig, List>> possibleFormatters, ErrorManager errorManager, - List filterElements, LaunchMode launchMode) { + LogCleanupFilter cleanupFilter, LaunchMode launchMode) { Map namedHandlers = new HashMap<>(); for (Entry consoleConfigEntry : config.consoleHandlers.entrySet()) { ConsoleConfig namedConsoleConfig = consoleConfigEntry.getValue(); @@ -270,7 +277,7 @@ private static Map createNamedHandlers(LogConfig config, Consol continue; } final Handler consoleHandler = configureConsoleHandler(namedConsoleConfig, consoleRuntimeConfig, errorManager, - filterElements, + cleanupFilter, possibleFormatters, null, launchMode); addToNamedHandlers(namedHandlers, consoleHandler, consoleConfigEntry.getKey()); } @@ -279,7 +286,7 @@ private static Map createNamedHandlers(LogConfig config, Consol if (!namedFileConfig.enable) { continue; } - final Handler fileHandler = configureFileHandler(namedFileConfig, errorManager, filterElements); + final Handler fileHandler = configureFileHandler(namedFileConfig, errorManager, cleanupFilter); addToNamedHandlers(namedHandlers, fileHandler, fileConfigEntry.getKey()); } for (Entry sysLogConfigEntry : config.syslogHandlers.entrySet()) { @@ -287,7 +294,7 @@ private static Map createNamedHandlers(LogConfig config, Consol if (!namedSyslogConfig.enable) { continue; } - final Handler syslogHandler = configureSyslogHandler(namedSyslogConfig, errorManager, filterElements); + final Handler syslogHandler = configureSyslogHandler(namedSyslogConfig, errorManager, cleanupFilter); if (syslogHandler != null) { addToNamedHandlers(namedHandlers, syslogHandler, sysLogConfigEntry.getKey()); } @@ -341,7 +348,7 @@ public void initializeLoggingForImageBuild() { private static Handler configureConsoleHandler(final ConsoleConfig config, ConsoleRuntimeConfig consoleRuntimeConfig, final ErrorManager defaultErrorManager, - final List filterElements, + final LogCleanupFilter cleanupFilter, final List>> possibleFormatters, final RuntimeValue>> possibleBannerSupplier, LaunchMode launchMode) { Formatter formatter = null; @@ -384,7 +391,7 @@ private static Handler configureConsoleHandler(final ConsoleConfig config, Conso config.stderr ? ConsoleHandler.Target.SYSTEM_ERR : ConsoleHandler.Target.SYSTEM_OUT, formatter); consoleHandler.setLevel(config.level); consoleHandler.setErrorManager(defaultErrorManager); - consoleHandler.setFilter(new LogCleanupFilter(filterElements)); + consoleHandler.setFilter(cleanupFilter); Handler handler = config.async.enable ? createAsyncHandler(config.async, config.level, consoleHandler) : consoleHandler; @@ -421,7 +428,7 @@ public void close() throws SecurityException { } private static Handler configureFileHandler(final FileConfig config, final ErrorManager errorManager, - final List filterElements) { + final LogCleanupFilter cleanupFilter) { FileHandler handler = new FileHandler(); FileConfig.RotationConfig rotationConfig = config.rotation; if ((rotationConfig.maxFileSize.isPresent() || rotationConfig.rotateOnBoot) @@ -454,7 +461,7 @@ private static Handler configureFileHandler(final FileConfig config, final Error } handler.setErrorManager(errorManager); handler.setLevel(config.level); - handler.setFilter(new LogCleanupFilter(filterElements)); + handler.setFilter(cleanupFilter); if (config.async.enable) { return createAsyncHandler(config.async, config.level, handler); } @@ -463,7 +470,7 @@ private static Handler configureFileHandler(final FileConfig config, final Error private static Handler configureSyslogHandler(final SyslogConfig config, final ErrorManager errorManager, - final List filterElements) { + final LogCleanupFilter logCleanupFilter) { try { final SyslogHandler handler = new SyslogHandler(config.endpoint.getHostString(), config.endpoint.getPort()); handler.setAppName(config.appName.orElse(getProcessName())); @@ -478,7 +485,7 @@ private static Handler configureSyslogHandler(final SyslogConfig config, final PatternFormatter formatter = new PatternFormatter(config.format); handler.setFormatter(formatter); handler.setErrorManager(errorManager); - handler.setFilter(new LogCleanupFilter(filterElements)); + handler.setFilter(logCleanupFilter); if (config.async.enable) { return createAsyncHandler(config.async, config.level, handler); } diff --git a/extensions/hibernate-reactive/deployment/pom.xml b/extensions/hibernate-reactive/deployment/pom.xml index 20f36105890543..283a0b43c7c4af 100644 --- a/extensions/hibernate-reactive/deployment/pom.xml +++ b/extensions/hibernate-reactive/deployment/pom.xml @@ -73,7 +73,7 @@ io.quarkus - quarkus-resteasy-deployment + quarkus-resteasy-reactive-jsonb-deployment test diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/Fruit.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/Fruit.java new file mode 100644 index 00000000000000..f97af4fa431fa8 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/Fruit.java @@ -0,0 +1,51 @@ +package io.quarkus.hibernate.reactive.dev; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.NamedQuery; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +@Entity +@Table(name = "known_fruits") +@NamedQuery(name = "Fruits.findAll", query = "SELECT f FROM Fruit f ORDER BY f.name") +public class Fruit { + + @Id + @SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10) + @GeneratedValue(generator = "fruitsSequence") + private Integer id; + + @Column(length = 40, unique = true) + private String name; + + public Fruit() { + } + + public Fruit(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Fruit{" + id + "," + name + '}'; + } +} diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/FruitMutinyResource.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/FruitMutinyResource.java new file mode 100644 index 00000000000000..e05664860e2f15 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/FruitMutinyResource.java @@ -0,0 +1,139 @@ +package io.quarkus.hibernate.reactive.dev; + +import static javax.ws.rs.core.Response.Status.CREATED; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static javax.ws.rs.core.Response.Status.NO_CONTENT; + +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.hibernate.reactive.mutiny.Mutiny; +import org.jboss.logging.Logger; +import org.jboss.resteasy.reactive.RestPath; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import io.smallrye.mutiny.Uni; + +@Path("fruits") +@ApplicationScoped +@Produces("application/json") +@Consumes("application/json") +public class FruitMutinyResource { + private static final Logger LOGGER = Logger.getLogger(FruitMutinyResource.class); + + @Inject + Mutiny.SessionFactory sf; + + @GET + public Uni> get() { + return sf.withTransaction((s, t) -> s + .createNamedQuery("Fruits.findAll", Fruit.class) + .getResultList()); + } + + @GET + @Path("{id}") + public Uni getSingle(@RestPath Integer id) { + return sf.withTransaction((s, t) -> s.find(Fruit.class, id)); + } + + @POST + public Uni create(Fruit fruit) { + if (fruit == null || fruit.getId() != null) { + throw new WebApplicationException("Id was invalidly set on request.", 422); + } + + return sf.withTransaction((s, t) -> s.persist(fruit)) + .replaceWith(() -> Response.ok(fruit).status(CREATED).build()); + } + + @PUT + @Path("{id}") + public Uni update(@RestPath Integer id, Fruit fruit) { + if (fruit == null || fruit.getName() == null) { + throw new WebApplicationException("Fruit name was not set on request.", 422); + } + + return sf.withTransaction((s, t) -> s.find(Fruit.class, id) + // If entity exists then update it + .onItem().ifNotNull().invoke(entity -> entity.setName(fruit.getName())) + .onItem().ifNotNull().transform(entity -> Response.ok(entity).build()) + // If entity not found return the appropriate response + .onItem().ifNull() + .continueWith(() -> Response.ok().status(NOT_FOUND).build())); + } + + @DELETE + @Path("{id}") + public Uni delete(@RestPath Integer id) { + return sf.withTransaction((s, t) -> s.find(Fruit.class, id) + // If entity exists then delete it + .onItem().ifNotNull() + .transformToUni(entity -> s.remove(entity) + .replaceWith(() -> Response.ok().status(NO_CONTENT).build())) + // If entity not found return the appropriate response + .onItem().ifNull().continueWith(() -> Response.ok().status(NOT_FOUND).build())); + } + + /** + * Create a HTTP response from an exception. + * + * Response Example: + * + *
+     * HTTP/1.1 422 Unprocessable Entity
+     * Content-Length: 111
+     * Content-Type: application/json
+     *
+     * {
+     *     "code": 422,
+     *     "error": "Fruit name was not set on request.",
+     *     "exceptionType": "javax.ws.rs.WebApplicationException"
+     * }
+     * 
+ */ + @Provider + public static class ErrorMapper implements ExceptionMapper { + + @Inject + ObjectMapper objectMapper; + + @Override + public Response toResponse(Exception exception) { + LOGGER.error("Failed to handle request", exception); + + int code = 500; + if (exception instanceof WebApplicationException) { + code = ((WebApplicationException) exception).getResponse().getStatus(); + } + + ObjectNode exceptionJson = objectMapper.createObjectNode(); + exceptionJson.put("exceptionType", exception.getClass().getName()); + exceptionJson.put("code", code); + + if (exception.getMessage() != null) { + exceptionJson.put("error", exception.getMessage()); + } + + return Response.status(code) + .entity(exceptionJson) + .build(); + } + + } +} diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/HibernateReactiveDevModeTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/HibernateReactiveDevModeTest.java new file mode 100644 index 00000000000000..c6b405936eddcb --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/dev/HibernateReactiveDevModeTest.java @@ -0,0 +1,59 @@ +package io.quarkus.hibernate.reactive.dev; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.response.Response; + +/** + * Checks that public field access is correctly replaced with getter/setter calls, + * regardless of the field type. + */ +public class HibernateReactiveDevModeTest { + + @RegisterExtension + static QuarkusDevModeTest runner = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(Fruit.class, FruitMutinyResource.class).addAsResource("application.properties") + .addAsResource(new StringAsset("INSERT INTO known_fruits(id, name) VALUES (1, 'Cherry');\n" + + "INSERT INTO known_fruits(id, name) VALUES (2, 'Apple');\n" + + "INSERT INTO known_fruits(id, name) VALUES (3, 'Banana');\n"), "import.sql")); + + @Test + public void testListAllFruits() { + Response response = given() + .when() + .get("/fruits") + .then() + .statusCode(200) + .contentType("application/json") + .extract().response(); + assertThat(response.jsonPath().getList("name")).isEqualTo(Arrays.asList("Apple", "Banana", "Cherry")); + + runner.modifySourceFile(Fruit.class, s -> s.replace("ORDER BY f.name", "ORDER BY f.name desk")); + given() + .when() + .get("/fruits") + .then() + .statusCode(500); + + runner.modifySourceFile(Fruit.class, s -> s.replace("desk", "desc")); + response = given() + .when() + .get("/fruits") + .then() + .statusCode(200) + .contentType("application/json") + .extract().response(); + assertThat(response.jsonPath().getList("name")).isEqualTo(Arrays.asList("Cherry", "Banana", "Apple")); + } +} diff --git a/extensions/vertx-core/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java b/extensions/vertx-core/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java index b932f00cfaf680..e6f6e1fef3c0f7 100644 --- a/extensions/vertx-core/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java +++ b/extensions/vertx-core/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java @@ -72,6 +72,11 @@ EventLoopCountBuildItem eventLoopCount(VertxCoreRecorder recorder, VertxConfigur return new EventLoopCountBuildItem(recorder.calculateEventLoopThreads(vertxConfiguration)); } + @BuildStep + LogCleanupFilterBuildItem cleanupVertxWarnings() { + return new LogCleanupFilterBuildItem("io.vertx.core.impl.ContextImpl", "You have disabled TCCL checks"); + } + @BuildStep @Record(ExecutionTime.STATIC_INIT) IOThreadDetectorBuildItem ioThreadDetector(VertxCoreRecorder recorder) { diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java index a74e994fde389f..41d3a861a798e7 100644 --- a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java @@ -64,6 +64,10 @@ @Recorder public class VertxCoreRecorder { + static { + System.setProperty("vertx.disableTCCL", "true"); + } + private static final Logger LOGGER = Logger.getLogger(VertxCoreRecorder.class.getName()); public static final String VERTX_CACHE = "vertx-cache"; diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java index 2a71daa94ac94e..7f989071203114 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java @@ -262,6 +262,7 @@ public void afterAll(ExtensionContext context) throws Exception { } rootLogger.setHandlers(originalRootLoggerHandlers); inMemoryLogHandler.clearRecords(); + inMemoryLogHandler.setFilter(null); ClearCache.clearAnnotationCache(); GroovyCacheCleaner.clearGroovyCache(); } diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java index a0f32b1ec076fa..693eb2957b69b8 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java @@ -574,6 +574,7 @@ public void afterAll(ExtensionContext extensionContext) throws Exception { } rootLogger.setHandlers(originalHandlers); inMemoryLogHandler.clearRecords(); + inMemoryLogHandler.setFilter(null); try { if (runningQuarkusApplication != null) {