> supportedSubprotocols();
/**
- * TODO Not implemented yet.
- *
- * The default timeout to complete processing of a message.
+ * Compression Extensions for WebSocket are supported by default.
+ *
+ * See also RFC 7692
*/
- Optional timeout();
+ @WithDefault("true")
+ boolean perMessageCompressionSupported();
+
+ /**
+ * The compression level must be a value between 0 and 9. The default value is
+ * {@value HttpServerOptions#DEFAULT_WEBSOCKET_COMPRESSION_LEVEL}.
+ */
+ OptionalInt compressionLevel();
+
+ /**
+ * The maximum size of a message in bytes. The default values is
+ * {@value HttpServerOptions#DEFAULT_MAX_WEBSOCKET_MESSAGE_SIZE}.
+ */
+ OptionalInt maxMessageSize();
}
diff --git a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/ContextSupport.java b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/ContextSupport.java
index 6edb66693f906..9e4b7a4e81525 100644
--- a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/ContextSupport.java
+++ b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/ContextSupport.java
@@ -69,7 +69,7 @@ void endSession() {
}
ContextState currentRequestContextState() {
- return requestContext.getState();
+ return requestContext.getStateIfActive();
}
static Context createNewDuplicatedContext(Context context, WebSocketConnection connection) {
diff --git a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpoint.java b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpoint.java
index 5ad60e04a69dd..a5dfb70a86076 100644
--- a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpoint.java
+++ b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpoint.java
@@ -79,6 +79,13 @@ default ExecutionModel onCloseExecutionModel() {
Uni doOnError(Throwable t);
+ /**
+ *
+ * @return the identifier of the bean with callbacks
+ * @see io.quarkus.arc.InjectableBean#getIdentifier()
+ */
+ String beanIdentifier();
+
enum ExecutionModel {
WORKER_THREAD,
VIRTUAL_THREAD,
diff --git a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpointBase.java b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpointBase.java
index 051362461babe..261de140f1683 100644
--- a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpointBase.java
+++ b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketEndpointBase.java
@@ -5,10 +5,14 @@
import java.util.function.Consumer;
import java.util.function.Function;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Singleton;
+
import org.jboss.logging.Logger;
import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
+import io.quarkus.arc.InjectableBean;
import io.quarkus.arc.InjectableContext.ContextState;
import io.quarkus.virtual.threads.VirtualThreadsRecorder;
import io.quarkus.websockets.next.WebSocket.ExecutionMode;
@@ -43,6 +47,9 @@ public abstract class WebSocketEndpointBase implements WebSocketEndpoint {
private final ContextSupport contextSupport;
+ private final InjectableBean> bean;
+ private final Object beanInstance;
+
public WebSocketEndpointBase(WebSocketConnection connection, Codecs codecs,
WebSocketsRuntimeConfig config, ContextSupport contextSupport) {
this.connection = connection;
@@ -51,6 +58,16 @@ public WebSocketEndpointBase(WebSocketConnection connection, Codecs codecs,
this.config = config;
this.container = Arc.container();
this.contextSupport = contextSupport;
+ InjectableBean> bean = container.bean(beanIdentifier());
+ if (bean.getScope().equals(ApplicationScoped.class)
+ || bean.getScope().equals(Singleton.class)) {
+ // For certain scopes, we can optimize and obtain the contextual reference immediately
+ this.bean = null;
+ this.beanInstance = container.instance(bean).get();
+ } else {
+ this.bean = bean;
+ this.beanInstance = null;
+ }
}
@Override
@@ -238,6 +255,10 @@ public void handle(Void event) {
return UniHelper.toUni(promise.future());
}
+ public Object beanInstance() {
+ return beanInstance != null ? beanInstance : container.instance(bean).get();
+ }
+
public Object beanInstance(String identifier) {
return container.instance(container.bean(identifier)).get();
}
diff --git a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketHttpServerOptionsCustomizer.java b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketHttpServerOptionsCustomizer.java
index 5018b1aee2b35..5233fd4a1cc34 100644
--- a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketHttpServerOptionsCustomizer.java
+++ b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketHttpServerOptionsCustomizer.java
@@ -17,12 +17,23 @@ public class WebSocketHttpServerOptionsCustomizer implements HttpServerOptionsCu
@Override
public void customizeHttpServer(HttpServerOptions options) {
- config.supportedSubprotocols().orElse(List.of()).forEach(options::addWebSocketSubProtocol);
+ customize(options);
}
@Override
public void customizeHttpsServer(HttpServerOptions options) {
+ customize(options);
+ }
+
+ private void customize(HttpServerOptions options) {
config.supportedSubprotocols().orElse(List.of()).forEach(options::addWebSocketSubProtocol);
+ options.setPerMessageWebSocketCompressionSupported(config.perMessageCompressionSupported());
+ if (config.compressionLevel().isPresent()) {
+ options.setWebSocketCompressionLevel(config.compressionLevel().getAsInt());
+ }
+ if (config.maxMessageSize().isPresent()) {
+ options.setMaxWebSocketMessageSize(config.maxMessageSize().getAsInt());
+ }
}
}
diff --git a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketServerRecorder.java b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketServerRecorder.java
index a0b98d13b1209..c53d15645b01d 100644
--- a/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketServerRecorder.java
+++ b/extensions/websockets-next/server/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketServerRecorder.java
@@ -234,6 +234,21 @@ public void handle(Void event) {
});
}
});
+
+ ws.exceptionHandler(new Handler() {
+ @Override
+ public void handle(Throwable t) {
+ ContextSupport.createNewDuplicatedContext(context, connection).runOnContext(new Handler() {
+ @Override
+ public void handle(Void event) {
+ endpoint.doOnError(t).subscribe().with(
+ v -> LOG.debugf("Error [%s] processed: %s", t.getClass(), connection),
+ t -> LOG.errorf(t, "Unhandled error occured: %s", t.toString(),
+ connection));
+ }
+ });
+ }
+ });
});
}
};
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifact.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifact.java
index 23d5751479c88..c52eacf3cdf7f 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifact.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifact.java
@@ -2,8 +2,11 @@
import java.io.Serializable;
import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
import io.quarkus.bootstrap.workspace.WorkspaceModule;
+import io.quarkus.maven.dependency.ArtifactCoords;
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.paths.PathCollection;
import io.quarkus.paths.PathList;
@@ -121,4 +124,9 @@ public String getScope() {
public int getFlags() {
return flags;
}
+
+ @Override
+ public Collection getDependencies() {
+ return List.of();
+ }
}
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppDependency.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppDependency.java
index 4c74856d5f9b3..803b09b1df05e 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppDependency.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppDependency.java
@@ -1,8 +1,11 @@
package io.quarkus.bootstrap.model;
import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
import java.util.Objects;
+import io.quarkus.maven.dependency.ArtifactCoords;
import io.quarkus.maven.dependency.ArtifactKey;
import io.quarkus.maven.dependency.DependencyFlags;
import io.quarkus.maven.dependency.ResolvedDependency;
@@ -126,4 +129,9 @@ public ArtifactKey getKey() {
public PathCollection getResolvedPaths() {
return artifact.getResolvedPaths();
}
+
+ @Override
+ public Collection getDependencies() {
+ return List.of();
+ }
}
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModelBuilder.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModelBuilder.java
index 95784e1755be8..c4e0331313581 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModelBuilder.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/ApplicationModelBuilder.java
@@ -35,7 +35,7 @@ public class ApplicationModelBuilder {
private static final Logger log = Logger.getLogger(ApplicationModelBuilder.class);
- ResolvedDependency appArtifact;
+ ResolvedDependencyBuilder appArtifact;
final Map dependencies = new LinkedHashMap<>();
final Collection parentFirstArtifacts = new ConcurrentLinkedDeque<>();
@@ -56,11 +56,15 @@ public ApplicationModelBuilder() {
.build());
}
- public ApplicationModelBuilder setAppArtifact(ResolvedDependency appArtifact) {
+ public ApplicationModelBuilder setAppArtifact(ResolvedDependencyBuilder appArtifact) {
this.appArtifact = appArtifact;
return this;
}
+ public ResolvedDependencyBuilder getApplicationArtifact() {
+ return appArtifact;
+ }
+
public ApplicationModelBuilder setPlatformImports(PlatformImports platformImports) {
this.platformImports = platformImports;
return this;
@@ -77,10 +81,14 @@ public ApplicationModelBuilder addDependency(ResolvedDependencyBuilder dep) {
}
public ApplicationModelBuilder addDependencies(Collection deps) {
- deps.forEach(d -> addDependency(d));
+ deps.forEach(this::addDependency);
return this;
}
+ public boolean hasDependency(ArtifactKey key) {
+ return dependencies.containsKey(key);
+ }
+
public ResolvedDependencyBuilder getDependency(ArtifactKey key) {
return dependencies.get(key);
}
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java
index b03ebc134dbcb..1796b1ceff51a 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/DefaultApplicationModel.java
@@ -26,7 +26,7 @@ public class DefaultApplicationModel implements ApplicationModel, Serializable {
private final Map> excludedResources;
public DefaultApplicationModel(ApplicationModelBuilder builder) {
- this.appArtifact = builder.appArtifact;
+ this.appArtifact = builder.appArtifact.build();
this.dependencies = builder.buildDependencies();
this.platformImports = builder.platformImports;
this.capabilityContracts = List.copyOf(builder.extensionCapabilities);
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/MutableJarApplicationModel.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/MutableJarApplicationModel.java
index b705dac6dde86..ebceb7d97089f 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/MutableJarApplicationModel.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/MutableJarApplicationModel.java
@@ -59,7 +59,7 @@ public String getUserProvidersDirectory() {
public ApplicationModel getAppModel(Path root) {
final ApplicationModelBuilder model = new ApplicationModelBuilder();
- model.setAppArtifact(appArtifact.getDep(root).build());
+ model.setAppArtifact(appArtifact.getDep(root));
for (SerializedDep i : dependencies) {
model.addDependency(i.getDep(root));
}
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/Dependency.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/Dependency.java
index 8fe5601ca64dc..2a95f37474117 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/Dependency.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/Dependency.java
@@ -8,15 +8,15 @@ public interface Dependency extends ArtifactCoords {
String SCOPE_COMPILE = "compile";
String SCOPE_IMPORT = "import";
- public static Dependency of(String groupId, String artifactId) {
+ static Dependency of(String groupId, String artifactId) {
return new ArtifactDependency(groupId, artifactId, null, ArtifactCoords.TYPE_JAR, null);
}
- public static Dependency of(String groupId, String artifactId, String version) {
+ static Dependency of(String groupId, String artifactId, String version) {
return new ArtifactDependency(groupId, artifactId, null, ArtifactCoords.TYPE_JAR, version);
}
- public static Dependency pomImport(String groupId, String artifactId, String version) {
+ static Dependency pomImport(String groupId, String artifactId, String version) {
return new ArtifactDependency(groupId, artifactId, null, ArtifactCoords.TYPE_POM, version, SCOPE_IMPORT, false);
}
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedArtifactDependency.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedArtifactDependency.java
index e8788cd70f53b..25964852a2b78 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedArtifactDependency.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedArtifactDependency.java
@@ -2,6 +2,8 @@
import java.io.Serializable;
import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
import java.util.Objects;
import io.quarkus.bootstrap.workspace.WorkspaceModule;
@@ -14,7 +16,8 @@ public class ResolvedArtifactDependency extends ArtifactDependency implements Re
private static final long serialVersionUID = 4038042391733012566L;
private PathCollection paths;
- private WorkspaceModule module;
+ private final WorkspaceModule module;
+ private final Collection deps;
private volatile transient PathTree contentTree;
public ResolvedArtifactDependency(ArtifactCoords coords) {
@@ -34,17 +37,22 @@ public ResolvedArtifactDependency(String groupId, String artifactId, String clas
PathCollection resolvedPath) {
super(groupId, artifactId, classifier, type, version);
this.paths = resolvedPath;
+ this.module = null;
+ this.deps = List.of();
}
public ResolvedArtifactDependency(ArtifactCoords coords, PathCollection resolvedPaths) {
super(coords);
this.paths = resolvedPaths;
+ this.module = null;
+ this.deps = List.of();
}
public ResolvedArtifactDependency(ResolvedDependencyBuilder builder) {
- super(builder);
+ super((AbstractDependencyBuilder, ?>) builder);
this.paths = builder.getResolvedPaths();
this.module = builder.getWorkspaceModule();
+ this.deps = builder.getDependencies();
}
@Override
@@ -66,6 +74,11 @@ public PathTree getContentTree() {
return contentTree == null ? contentTree = ResolvableDependency.super.getContentTree() : contentTree;
}
+ @Override
+ public Collection getDependencies() {
+ return deps;
+ }
+
@Override
public int hashCode() {
final int prime = 31;
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java
index e22e23a961671..03701e7c46b3e 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java
@@ -1,6 +1,7 @@
package io.quarkus.maven.dependency;
import java.nio.file.Path;
+import java.util.Collection;
import io.quarkus.bootstrap.workspace.ArtifactSources;
import io.quarkus.bootstrap.workspace.WorkspaceModule;
@@ -15,6 +16,8 @@ public interface ResolvedDependency extends Dependency {
PathCollection getResolvedPaths();
+ Collection getDependencies();
+
default boolean isResolved() {
final PathCollection paths = getResolvedPaths();
return paths != null && !paths.isEmpty();
diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependencyBuilder.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependencyBuilder.java
index 551898e1f7d8f..1f6d115bcc84a 100644
--- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependencyBuilder.java
+++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependencyBuilder.java
@@ -1,12 +1,16 @@
package io.quarkus.maven.dependency;
import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
import io.quarkus.bootstrap.workspace.WorkspaceModule;
import io.quarkus.paths.PathCollection;
import io.quarkus.paths.PathList;
-public class ResolvedDependencyBuilder extends AbstractDependencyBuilder {
+public class ResolvedDependencyBuilder extends AbstractDependencyBuilder
+ implements ResolvedDependency {
public static ResolvedDependencyBuilder newInstance() {
return new ResolvedDependencyBuilder();
@@ -15,7 +19,9 @@ public static ResolvedDependencyBuilder newInstance() {
PathCollection resolvedPaths;
WorkspaceModule workspaceModule;
private volatile ArtifactCoords coords;
+ private Set deps = Set.of();
+ @Override
public PathCollection getResolvedPaths() {
return resolvedPaths;
}
@@ -30,6 +36,7 @@ public ResolvedDependencyBuilder setResolvedPaths(PathCollection resolvedPaths)
return this;
}
+ @Override
public WorkspaceModule getWorkspaceModule() {
return workspaceModule;
}
@@ -46,6 +53,32 @@ public ArtifactCoords getArtifactCoords() {
return coords == null ? coords = ArtifactCoords.of(groupId, artifactId, classifier, type, version) : coords;
}
+ public ResolvedDependencyBuilder addDependency(ArtifactCoords coords) {
+ if (coords != null) {
+ if (deps.isEmpty()) {
+ deps = new HashSet<>();
+ }
+ deps.add(coords);
+ }
+ return this;
+ }
+
+ public ResolvedDependencyBuilder addDependencies(Collection deps) {
+ if (!deps.isEmpty()) {
+ if (this.deps.isEmpty()) {
+ this.deps = new HashSet<>(deps);
+ } else {
+ this.deps.addAll(deps);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public Collection getDependencies() {
+ return deps;
+ }
+
@Override
public ResolvedDependency build() {
return new ResolvedArtifactDependency(this);
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapAppModelFactory.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapAppModelFactory.java
index e7b1c5a1e4c82..b89b39355fcc0 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapAppModelFactory.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapAppModelFactory.java
@@ -24,6 +24,7 @@
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver;
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext;
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContextConfig;
+import io.quarkus.bootstrap.resolver.maven.IncubatingApplicationModelResolver;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalWorkspace;
@@ -152,24 +153,34 @@ public AppModelResolver getAppModelResolver() {
} else {
mvn = mavenArtifactResolver;
}
-
- return bootstrapAppModelResolver = new BootstrapAppModelResolver(mvn)
- .setTest(test)
- .setDevMode(devMode);
+ return bootstrapAppModelResolver = initAppModelResolver(mvn);
}
MavenArtifactResolver mvn = mavenArtifactResolver;
if (mvn == null) {
mvn = new MavenArtifactResolver(createBootstrapMavenContext());
}
- return bootstrapAppModelResolver = new BootstrapAppModelResolver(mvn)
- .setTest(test)
- .setDevMode(devMode);
+ return bootstrapAppModelResolver = initAppModelResolver(mvn);
} catch (Exception e) {
throw new RuntimeException("Failed to create application model resolver for " + projectRoot, e);
}
}
+ private BootstrapAppModelResolver initAppModelResolver(MavenArtifactResolver artifactResolver) {
+ var appModelResolver = new BootstrapAppModelResolver(artifactResolver)
+ .setTest(test)
+ .setDevMode(devMode);
+ var project = artifactResolver.getMavenContext().getCurrentProject();
+ if (project != null) {
+ appModelResolver.setIncubatingModelResolver(
+ IncubatingApplicationModelResolver.isIncubatingEnabled(
+ project.getModelBuildingResult() == null
+ ? project.getRawModel().getProperties()
+ : project.getModelBuildingResult().getEffectiveModel().getProperties()));
+ }
+ return appModelResolver;
+ }
+
private BootstrapMavenContext createBootstrapMavenContext() throws AppModelResolverException {
if (mvnContext != null) {
return mvnContext;
diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java
index 5a5d2666f48e5..d8861642c2171 100644
--- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java
+++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/resolver/ResolverSetupCleanup.java
@@ -18,8 +18,8 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
-import io.quarkus.bootstrap.resolver.maven.ApplicationDependencyModelResolver;
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException;
+import io.quarkus.bootstrap.resolver.maven.IncubatingApplicationModelResolver;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject;
import io.quarkus.bootstrap.util.IoUtils;
@@ -149,7 +149,7 @@ protected boolean isBootstrapForTestMode() {
protected BootstrapAppModelResolver newAppModelResolver(LocalProject currentProject) throws Exception {
final BootstrapAppModelResolver appModelResolver = new BootstrapAppModelResolver(newArtifactResolver(currentProject));
- appModelResolver.setIncubatingModelResolver(ApplicationDependencyModelResolver.isIncubatingEnabled(null));
+ appModelResolver.setIncubatingModelResolver(IncubatingApplicationModelResolver.isIncubatingEnabled(null));
if (isBootstrapForTestMode()) {
appModelResolver.setTest(true);
}
diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java
index 037ad003fea3f..050846b40ae72 100644
--- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java
+++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java
@@ -34,10 +34,10 @@
import io.quarkus.bootstrap.BootstrapDependencyProcessingException;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.bootstrap.model.ApplicationModelBuilder;
-import io.quarkus.bootstrap.resolver.maven.ApplicationDependencyModelResolver;
import io.quarkus.bootstrap.resolver.maven.ApplicationDependencyTreeResolver;
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException;
import io.quarkus.bootstrap.resolver.maven.DependencyLoggingConfig;
+import io.quarkus.bootstrap.resolver.maven.IncubatingApplicationModelResolver;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.util.DependencyUtils;
import io.quarkus.bootstrap.workspace.ArtifactSources;
@@ -127,8 +127,21 @@ public void relink(ArtifactCoords artifact, Path path) throws AppModelResolverEx
}
@Override
- public ResolvedDependency resolve(ArtifactCoords artifact) throws AppModelResolverException {
- return resolve(artifact, toAetherArtifact(artifact), mvn.getRepositories());
+ public ResolvedDependency resolve(ArtifactCoords coords) throws AppModelResolverException {
+ final ResolvedDependency resolvedArtifact = ResolvedDependency.class.isAssignableFrom(coords.getClass())
+ ? (ResolvedDependency) coords
+ : null;
+ if (resolvedArtifact != null
+ && (resolvedArtifact.getWorkspaceModule() != null || mvn.getProjectModuleResolver() == null)) {
+ return resolvedArtifact;
+ }
+ final WorkspaceModule resolvedModule = mvn.getProjectModuleResolver() == null ? null
+ : mvn.getProjectModuleResolver().getProjectModule(coords.getGroupId(), coords.getArtifactId(),
+ coords.getVersion());
+ if (resolvedArtifact != null && resolvedModule == null) {
+ return resolvedArtifact;
+ }
+ return resolve(coords, toAetherArtifact(coords), mvn.getRepositories()).build();
}
@Override
@@ -215,15 +228,14 @@ public ApplicationModel resolveModel(WorkspaceModule module)
final Artifact mainArtifact = new DefaultArtifact(module.getId().getGroupId(), module.getId().getArtifactId(), null,
ArtifactCoords.TYPE_JAR,
module.getId().getVersion());
- final ResolvedDependency mainDep = ResolvedDependencyBuilder.newInstance()
+ final ResolvedDependencyBuilder mainDep = ResolvedDependencyBuilder.newInstance()
.setGroupId(mainArtifact.getGroupId())
.setArtifactId(mainArtifact.getArtifactId())
.setClassifier(mainArtifact.getClassifier())
.setType(mainArtifact.getExtension())
.setVersion(mainArtifact.getVersion())
.setResolvedPaths(resolvedPaths.build())
- .setWorkspaceModule(module)
- .build();
+ .setWorkspaceModule(module);
final Map managedMap = new HashMap<>();
for (io.quarkus.maven.dependency.Dependency d : module.getDirectDependencyConstraints()) {
@@ -276,7 +288,7 @@ private ApplicationModel doResolveModel(ArtifactCoords coords,
}
List aggregatedRepos = mvn.aggregateRepositories(managedRepos, mvn.getRepositories());
- final ResolvedDependency appArtifact = resolve(coords, mvnArtifact, aggregatedRepos);
+ final ResolvedDependencyBuilder appArtifact = resolve(coords, mvnArtifact, aggregatedRepos);
mvnArtifact = toAetherArtifact(appArtifact);
final ArtifactDescriptorResult appArtifactDescr = resolveDescriptor(mvnArtifact, aggregatedRepos);
@@ -319,7 +331,7 @@ private Set getExcludedScopes() {
return Set.of(JavaScopes.PROVIDED, JavaScopes.TEST);
}
- private ApplicationModel buildAppModel(ResolvedDependency appArtifact,
+ private ApplicationModel buildAppModel(ResolvedDependencyBuilder appArtifact,
Artifact artifact, List directDeps, List repos,
Set reloadableModules, List managedDeps)
throws AppModelResolverException {
@@ -353,7 +365,7 @@ private ApplicationModel buildAppModel(ResolvedDependency appArtifact,
start = System.currentTimeMillis();
}
if (incubatingModelResolver) {
- ApplicationDependencyModelResolver.newInstance()
+ IncubatingApplicationModelResolver.newInstance()
.setArtifactResolver(mvn)
.setApplicationModelBuilder(appBuilder)
.setCollectReloadableModules(collectReloadableDeps && reloadableModules.isEmpty())
@@ -382,45 +394,43 @@ private ApplicationModel buildAppModel(ResolvedDependency appArtifact,
return appBuilder.build();
}
- private io.quarkus.maven.dependency.ResolvedDependency resolve(ArtifactCoords appArtifact, Artifact mvnArtifact,
+ private io.quarkus.maven.dependency.ResolvedDependencyBuilder resolve(ArtifactCoords coords, Artifact artifact,
List aggregatedRepos) throws BootstrapMavenException {
- final ResolvedDependency resolvedArtifact = ResolvedDependency.class.isAssignableFrom(appArtifact.getClass())
- ? (ResolvedDependency) appArtifact
- : null;
- if (resolvedArtifact != null
- && (resolvedArtifact.getWorkspaceModule() != null || mvn.getProjectModuleResolver() == null)) {
- return resolvedArtifact;
+ final ResolvedDependencyBuilder depBuilder;
+ if (ResolvedDependencyBuilder.class.isAssignableFrom(coords.getClass())) {
+ depBuilder = (ResolvedDependencyBuilder) coords;
+ } else {
+ depBuilder = ResolvedDependencyBuilder.newInstance().setCoords(coords);
+ if (coords instanceof ResolvedDependency resolved) {
+ depBuilder.setResolvedPaths(resolved.getResolvedPaths());
+ }
}
- final WorkspaceModule resolvedModule = mvn.getProjectModuleResolver() == null ? null
- : mvn.getProjectModuleResolver().getProjectModule(appArtifact.getGroupId(), appArtifact.getArtifactId(),
- appArtifact.getVersion());
- if (resolvedArtifact != null && resolvedModule == null) {
- return resolvedArtifact;
+ WorkspaceModule wsModule = depBuilder.getWorkspaceModule();
+ if (wsModule == null && mvn.getProjectModuleResolver() != null) {
+ wsModule = mvn.getProjectModuleResolver().getProjectModule(coords.getGroupId(), coords.getArtifactId(),
+ coords.getVersion());
+ depBuilder.setWorkspaceModule(wsModule);
}
- PathCollection resolvedPaths = null;
- if ((devmode || test) && resolvedModule != null) {
- final ArtifactSources artifactSources = resolvedModule.getSources(appArtifact.getClassifier());
+ PathCollection resolvedPaths = depBuilder.getResolvedPaths();
+ if ((devmode || test) && wsModule != null) {
+ final ArtifactSources artifactSources = wsModule.getSources(coords.getClassifier());
if (artifactSources != null) {
final PathList.Builder pathBuilder = PathList.builder();
collectSourceDirs(pathBuilder, artifactSources.getSourceDirs());
collectSourceDirs(pathBuilder, artifactSources.getResourceDirs());
if (!pathBuilder.isEmpty()) {
resolvedPaths = pathBuilder.build();
+ depBuilder.setResolvedPaths(resolvedPaths);
}
}
}
- if (resolvedPaths == null) {
- if (resolvedArtifact == null || resolvedArtifact.getResolvedPaths() == null) {
- resolvedPaths = PathList.of(resolve(mvnArtifact, aggregatedRepos).getArtifact().getFile().toPath());
- } else {
- resolvedPaths = resolvedArtifact.getResolvedPaths();
- }
+ if (resolvedPaths == null || resolvedPaths.isEmpty()) {
+ depBuilder.setResolvedPaths(PathList.of(resolve(artifact, aggregatedRepos).getArtifact().getFile().toPath()));
}
- return ResolvedDependencyBuilder.newInstance().setCoords(appArtifact).setWorkspaceModule(resolvedModule)
- .setResolvedPaths(resolvedPaths).build();
+ return depBuilder;
}
private static void collectSourceDirs(final PathList.Builder pathBuilder, Collection resources) {
diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java
index 025aa5413c781..2ca34bb6e91c0 100644
--- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java
+++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java
@@ -15,6 +15,7 @@
import org.eclipse.aether.graph.DependencyNode;
import io.quarkus.bootstrap.model.ApplicationModelBuilder;
+import io.quarkus.maven.dependency.ArtifactCoords;
import io.quarkus.maven.dependency.DependencyFlags;
public class BuildDependencyGraphVisitor {
@@ -112,7 +113,15 @@ private void consume(DependencyNode node) {
buf.append('\u2514').append('\u2500').append(' ');
}
}
- buf.append(node.getArtifact());
+ var a = node.getArtifact();
+ buf.append(a.getGroupId()).append(":").append(a.getArtifactId()).append(":");
+ if (!a.getClassifier().isEmpty()) {
+ buf.append(a.getClassifier()).append(":");
+ }
+ if (!ArtifactCoords.TYPE_JAR.equals(a.getExtension())) {
+ buf.append(a.getExtension()).append(":");
+ }
+ buf.append(a.getVersion());
if (!depth.isEmpty()) {
buf.append(" (").append(node.getDependency().getScope());
if (node.getDependency().isOptional()) {
diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyModelResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/IncubatingApplicationModelResolver.java
similarity index 82%
rename from independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyModelResolver.java
rename to independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/IncubatingApplicationModelResolver.java
index 9a3b9e1855b4e..0cc742766e0c7 100644
--- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyModelResolver.java
+++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/IncubatingApplicationModelResolver.java
@@ -17,14 +17,13 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Phaser;
import java.util.function.BiConsumer;
import org.eclipse.aether.DefaultRepositorySystemSession;
@@ -62,12 +61,13 @@
import io.quarkus.maven.dependency.ArtifactCoords;
import io.quarkus.maven.dependency.ArtifactKey;
import io.quarkus.maven.dependency.DependencyFlags;
+import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.maven.dependency.ResolvedDependencyBuilder;
import io.quarkus.paths.PathTree;
-public class ApplicationDependencyModelResolver {
+public class IncubatingApplicationModelResolver {
- private static final Logger log = Logger.getLogger(ApplicationDependencyModelResolver.class);
+ private static final Logger log = Logger.getLogger(IncubatingApplicationModelResolver.class);
private static final String QUARKUS_RUNTIME_ARTIFACT = "quarkus.runtime";
private static final String QUARKUS_EXTENSION_DEPENDENCY = "quarkus.ext";
@@ -95,8 +95,8 @@ public static boolean isIncubatingEnabled(Properties projectProperties) {
return Boolean.parseBoolean(value);
}
- public static ApplicationDependencyModelResolver newInstance() {
- return new ApplicationDependencyModelResolver();
+ public static IncubatingApplicationModelResolver newInstance() {
+ return new IncubatingApplicationModelResolver();
}
private final ExtensionInfo EXT_INFO_NONE = new ExtensionInfo();
@@ -105,8 +105,6 @@ public static ApplicationDependencyModelResolver newInstance() {
private final Map allExtensions = new ConcurrentHashMap<>();
private List conditionalDepsToProcess = new ArrayList<>();
- private final Map> artifactDeps = new HashMap<>();
-
private final Collection errors = new ConcurrentLinkedDeque<>();
private MavenArtifactResolver resolver;
@@ -116,22 +114,22 @@ public static ApplicationDependencyModelResolver newInstance() {
private DependencyLoggingConfig depLogging;
private List collectCompileOnly;
- public ApplicationDependencyModelResolver setArtifactResolver(MavenArtifactResolver resolver) {
+ public IncubatingApplicationModelResolver setArtifactResolver(MavenArtifactResolver resolver) {
this.resolver = resolver;
return this;
}
- public ApplicationDependencyModelResolver setApplicationModelBuilder(ApplicationModelBuilder appBuilder) {
+ public IncubatingApplicationModelResolver setApplicationModelBuilder(ApplicationModelBuilder appBuilder) {
this.appBuilder = appBuilder;
return this;
}
- public ApplicationDependencyModelResolver setCollectReloadableModules(boolean collectReloadableModules) {
+ public IncubatingApplicationModelResolver setCollectReloadableModules(boolean collectReloadableModules) {
this.collectReloadableModules = collectReloadableModules;
return this;
}
- public ApplicationDependencyModelResolver setDependencyLogging(DependencyLoggingConfig depLogging) {
+ public IncubatingApplicationModelResolver setDependencyLogging(DependencyLoggingConfig depLogging) {
this.depLogging = depLogging;
return this;
}
@@ -143,7 +141,7 @@ public ApplicationDependencyModelResolver setDependencyLogging(DependencyLogging
* @param collectCompileOnly compile-only dependencies to add to the model
* @return self
*/
- public ApplicationDependencyModelResolver setCollectCompileOnly(List collectCompileOnly) {
+ public IncubatingApplicationModelResolver setCollectCompileOnly(List collectCompileOnly) {
this.collectCompileOnly = collectCompileOnly;
return this;
}
@@ -206,13 +204,11 @@ private List activateConditionalDeps() {
private void processDeploymentDeps(DependencyNode root) {
var app = new AppDep(root);
- var futures = new ArrayList>();
- app.scheduleChildVisits(futures, AppDep::scheduleDeploymentVisit);
- CompletableFuture.allOf(futures.toArray(new CompletableFuture>[0])).join();
- if (logErrors()) {
- throw new RuntimeException(
- "Failed to process Quarkus application deployment dependencies, please see the errors logged above for more details.");
- }
+ var phaser = new Phaser(1);
+ app.scheduleChildVisits(phaser, AppDep::scheduleDeploymentVisit);
+ phaser.arriveAndAwaitAdvance();
+ assertNoErrors();
+ appBuilder.getApplicationArtifact().addDependencies(app.allDeps);
for (var d : app.children) {
d.addToModel();
}
@@ -222,74 +218,85 @@ private void processDeploymentDeps(DependencyNode root) {
}
}
- private boolean logErrors() {
+ private void assertNoErrors() {
if (!errors.isEmpty()) {
- log.error("The following errors were encountered while processing Quarkus application dependencies:");
+ var sb = new StringBuilder(
+ "The following errors were encountered while processing Quarkus application dependencies:");
+ log.error(sb);
var i = 1;
for (var error : errors) {
- log.error(i++ + ")", error);
+ var prefix = i++ + ")";
+ log.error(prefix, error);
+ sb.append(System.lineSeparator()).append(prefix).append(" ").append(error.getLocalizedMessage());
+ for (var e : error.getStackTrace()) {
+ sb.append(System.lineSeparator()).append(e);
+ if (e.getClassName().contains("io.quarkus")) {
+ break;
+ }
+ }
}
- return true;
+ throw new RuntimeException(sb.toString());
}
- return false;
}
private void injectDeployment(List activatedConditionalDeps) {
- final List> futures = new ArrayList<>(topExtensionDeps.size()
- + activatedConditionalDeps.size());
- for (ExtensionDependency extDep : topExtensionDeps) {
- futures.add(CompletableFuture.supplyAsync(() -> {
- var resolvedDep = appBuilder.getDependency(getKey(extDep.info.deploymentArtifact));
- if (resolvedDep == null) {
+ final ConcurrentLinkedDeque injectQueue = new ConcurrentLinkedDeque<>();
+ {
+ var phaser = new Phaser(1);
+ for (ExtensionDependency extDep : topExtensionDeps) {
+ phaser.register();
+ CompletableFuture.runAsync(() -> {
+ var resolvedDep = appBuilder.getDependency(getKey(extDep.info.deploymentArtifact));
try {
- extDep.collectDeploymentDeps();
- return () -> extDep.injectDeploymentNode(null);
+ if (resolvedDep == null) {
+ extDep.collectDeploymentDeps();
+ injectQueue.add(() -> extDep.injectDeploymentNode(null));
+ } else {
+ // if resolvedDep isn't null, it means the deployment artifact is on the runtime classpath
+ // in which case we also clear the reloadable flag on it, in case it's coming from the workspace
+ resolvedDep.clearFlag(DependencyFlags.RELOADABLE);
+ }
} catch (BootstrapDependencyProcessingException e) {
errors.add(e);
+ } finally {
+ phaser.arriveAndDeregister();
}
- } else {
- // if resolvedDep isn't null, it means the deployment artifact is on the runtime classpath
- // in which case we also clear the reloadable flag on it, in case it's coming from the workspace
- resolvedDep.clearFlag(DependencyFlags.RELOADABLE);
- }
- return null;
- }));
+ });
+ }
+ // non-conditional deployment branches should be added before the activated conditional ones to have consistent
+ // dependency graph structures
+ phaser.arriveAndAwaitAdvance();
+ assertNoErrors();
}
- // non-conditional deployment branches should be added before the activated conditional ones to have consistent
- // dependency graph structures
- CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
- if (errors.isEmpty() && !activatedConditionalDeps.isEmpty()) {
+ if (!activatedConditionalDeps.isEmpty()) {
+ var phaser = new Phaser(1);
for (ConditionalDependency cd : activatedConditionalDeps) {
- futures.add(CompletableFuture.supplyAsync(() -> {
- var resolvedDep = appBuilder.getDependency(getKey(cd.appDep.ext.info.deploymentArtifact));
- if (resolvedDep == null) {
- var extDep = cd.getExtensionDependency();
- try {
+ phaser.register();
+ CompletableFuture.runAsync(() -> {
+ var resolvedDep = appBuilder.getDependency(getKey(cd.conditionalDep.ext.info.deploymentArtifact));
+ try {
+ if (resolvedDep == null) {
+ var extDep = cd.getExtensionDependency();
extDep.collectDeploymentDeps();
- return () -> extDep.injectDeploymentNode(cd.appDep.ext.getParentDeploymentNode());
- } catch (BootstrapDependencyProcessingException e) {
- errors.add(e);
+ injectQueue.add(() -> extDep.injectDeploymentNode(cd.conditionalDep.ext.getParentDeploymentNode()));
+ } else {
+ // if resolvedDep isn't null, it means the deployment artifact is on the runtime classpath
+ // in which case we also clear the reloadable flag on it, in case it's coming from the workspace
+ resolvedDep.clearFlag(DependencyFlags.RELOADABLE);
}
- } else {
- // if resolvedDep isn't null, it means the deployment artifact is on the runtime classpath
- // in which case we also clear the reloadable flag on it, in case it's coming from the workspace
- resolvedDep.clearFlag(DependencyFlags.RELOADABLE);
+ } catch (BootstrapDependencyProcessingException e) {
+ errors.add(e);
+ } finally {
+ phaser.arriveAndDeregister();
}
- return null;
- }));
+ });
}
- }
- CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
- if (logErrors()) {
- throw new RuntimeException(
- "Failed to process Quarkus application deployment dependencies, please see the errors logged above for more details.");
+ phaser.arriveAndAwaitAdvance();
+ assertNoErrors();
}
- for (var future : futures) {
- var ext = future.getNow(null);
- if (ext != null) {
- ext.run();
- }
+ for (var inject : injectQueue) {
+ inject.run();
}
}
@@ -370,14 +377,15 @@ private void collectPlatformProperties() throws AppModelResolverException {
for (Dependency d : managedDeps) {
final Artifact artifact = d.getArtifact();
final String extension = artifact.getExtension();
- final String artifactId = artifact.getArtifactId();
if ("json".equals(extension)
- && artifactId.endsWith(BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX)) {
- platformReleases.addPlatformDescriptor(artifact.getGroupId(), artifactId, artifact.getClassifier(), extension,
+ && artifact.getArtifactId().endsWith(BootstrapConstants.PLATFORM_DESCRIPTOR_ARTIFACT_ID_SUFFIX)) {
+ platformReleases.addPlatformDescriptor(artifact.getGroupId(), artifact.getArtifactId(),
+ artifact.getClassifier(), extension,
artifact.getVersion());
} else if ("properties".equals(extension)
- && artifactId.endsWith(BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX)) {
- platformReleases.addPlatformProperties(artifact.getGroupId(), artifactId, artifact.getClassifier(), extension,
+ && artifact.getArtifactId().endsWith(BootstrapConstants.PLATFORM_PROPERTIES_ARTIFACT_ID_SUFFIX)) {
+ platformReleases.addPlatformProperties(artifact.getGroupId(), artifact.getArtifactId(),
+ artifact.getClassifier(), extension,
artifact.getVersion(), resolver.resolve(artifact).getArtifact().getFile().toPath());
}
}
@@ -385,12 +393,12 @@ private void collectPlatformProperties() throws AppModelResolverException {
}
private void clearReloadableFlag(ResolvedDependencyBuilder dep) {
- final Set deps = artifactDeps.get(dep.getArtifactCoords());
- if (deps == null || deps.isEmpty()) {
+ final Collection deps = dep.getDependencies();
+ if (deps.isEmpty()) {
return;
}
- for (ArtifactKey key : deps) {
- final ResolvedDependencyBuilder child = appBuilder.getDependency(key);
+ for (ArtifactCoords coords : deps) {
+ final ResolvedDependencyBuilder child = appBuilder.getDependency(coords.getKey());
if (child == null || child.isFlagSet(DependencyFlags.VISITED)) {
continue;
}
@@ -444,20 +452,18 @@ private boolean isRuntimeArtifact(ArtifactKey key) {
}
private void processRuntimeDeps(DependencyNode root) {
- final AppDep app = new AppDep(root);
- app.walkingFlags = COLLECT_TOP_EXTENSION_RUNTIME_NODES | COLLECT_DIRECT_DEPS;
+ final AppDep appRoot = new AppDep(root);
+ appRoot.walkingFlags = COLLECT_TOP_EXTENSION_RUNTIME_NODES | COLLECT_DIRECT_DEPS;
if (collectReloadableModules) {
- app.walkingFlags |= COLLECT_RELOADABLE_MODULES;
+ appRoot.walkingFlags |= COLLECT_RELOADABLE_MODULES;
}
- var futures = new ArrayList>();
- app.scheduleChildVisits(futures, AppDep::scheduleRuntimeVisit);
- CompletableFuture.allOf(futures.toArray(new CompletableFuture>[0])).join();
- if (logErrors()) {
- throw new RuntimeException(
- "Failed to process Quarkus application runtime dependencies, please see the errors logged above for more details.");
- }
- app.setChildFlags();
+ final Phaser phaser = new Phaser(1);
+ appRoot.scheduleChildVisits(phaser, AppDep::scheduleRuntimeVisit);
+ phaser.arriveAndAwaitAdvance();
+ assertNoErrors();
+ appBuilder.getApplicationArtifact().addDependencies(appRoot.allDeps);
+ appRoot.setChildFlags();
}
private class AppDep {
@@ -467,17 +473,20 @@ private class AppDep {
byte walkingFlags;
ResolvedDependencyBuilder resolvedDep;
final List children;
+ final List allDeps;
AppDep(DependencyNode node) {
this.parent = null;
this.node = node;
this.children = new ArrayList<>(node.getChildren().size());
+ this.allDeps = new ArrayList<>(node.getChildren().size());
}
AppDep(AppDep parent, DependencyNode node) {
this.parent = parent;
this.node = node;
this.children = new ArrayList<>(node.getChildren().size());
+ this.allDeps = new ArrayList<>(node.getChildren().size());
}
void addToModel() {
@@ -486,41 +495,48 @@ void addToModel() {
}
// this node is added after its children to stay compatible with the legacy impl
if (resolvedDep != null) {
+ resolvedDep.addDependencies(allDeps);
appBuilder.addDependency(resolvedDep);
}
}
- void scheduleDeploymentVisit(List> futures) {
- futures.add(CompletableFuture.runAsync(() -> {
+ void scheduleDeploymentVisit(Phaser phaser) {
+ phaser.register();
+ CompletableFuture.runAsync(() -> {
try {
visitDeploymentDependency();
} catch (Throwable e) {
errors.add(e);
+ } finally {
+ phaser.arriveAndDeregister();
}
- }));
- scheduleChildVisits(futures, AppDep::scheduleDeploymentVisit);
+ });
+ scheduleChildVisits(phaser, AppDep::scheduleDeploymentVisit);
}
void visitDeploymentDependency() {
- var dep = appBuilder.getDependency(getKey(node.getArtifact()));
- if (dep == null) {
+ if (!appBuilder.hasDependency(getKey(node.getArtifact()))) {
try {
- resolvedDep = newDependencyBuilder(node, resolver).setFlags(DependencyFlags.DEPLOYMENT_CP);
+ resolvedDep = newDependencyBuilder(node, resolver)
+ .setFlags(DependencyFlags.DEPLOYMENT_CP);
} catch (BootstrapMavenException e) {
throw new RuntimeException(e);
}
}
}
- void scheduleRuntimeVisit(List> futures) {
- futures.add(CompletableFuture.runAsync(() -> {
+ void scheduleRuntimeVisit(Phaser phaser) {
+ phaser.register();
+ CompletableFuture.runAsync(() -> {
try {
visitRuntimeDependency();
} catch (Throwable t) {
errors.add(t);
+ } finally {
+ phaser.arriveAndDeregister();
}
- }));
- scheduleChildVisits(futures, AppDep::scheduleRuntimeVisit);
+ });
+ scheduleChildVisits(phaser, AppDep::scheduleRuntimeVisit);
}
void visitRuntimeDependency() {
@@ -558,23 +574,21 @@ void visitRuntimeDependency() {
}
}
- void scheduleChildVisits(List> futures,
- BiConsumer>> childVisitor) {
+ void scheduleChildVisits(Phaser phaser, BiConsumer childVisitor) {
var childNodes = node.getChildren();
List filtered = null;
- var depKeys = artifactDeps.computeIfAbsent(getCoords(node.getArtifact()), key -> new HashSet<>(childNodes.size()));
for (int i = 0; i < childNodes.size(); ++i) {
var childNode = childNodes.get(i);
var winner = getWinner(childNode);
if (winner == null) {
- depKeys.add(getKey(childNode.getArtifact()));
+ allDeps.add(getCoords(childNode.getArtifact()));
var child = new AppDep(this, childNode);
children.add(child);
if (filtered != null) {
filtered.add(childNode);
}
} else {
- depKeys.add(getKey(winner.getArtifact()));
+ allDeps.add(getCoords(winner.getArtifact()));
if (filtered == null) {
filtered = new ArrayList<>(childNodes.size());
for (int j = 0; j < i; ++j) {
@@ -587,7 +601,7 @@ void scheduleChildVisits(List> futures,
node.setChildren(filtered);
}
for (var child : children) {
- childVisitor.accept(child, futures);
+ childVisitor.accept(child, phaser);
}
}
@@ -599,6 +613,7 @@ void setChildFlags() {
void setFlags(byte walkingFlags) {
+ resolvedDep.addDependencies(allDeps);
if (ext != null) {
var parentExtDep = parent;
while (parentExtDep != null) {
@@ -611,11 +626,14 @@ void setFlags(byte walkingFlags) {
ext.info.ensureActivated();
}
- if (appBuilder.getDependency(resolvedDep.getKey()) == null) {
+ var existingDep = appBuilder.getDependency(resolvedDep.getKey());
+ if (existingDep == null) {
appBuilder.addDependency(resolvedDep);
if (ext != null) {
managedDeps.add(new Dependency(ext.info.deploymentArtifact, JavaScopes.COMPILE));
}
+ } else if (existingDep != resolvedDep) {
+ throw new IllegalStateException(node.getArtifact() + " is already in the model");
}
this.walkingFlags = walkingFlags;
@@ -635,7 +653,6 @@ void setFlags(byte walkingFlags) {
}
clearWalkingFlag(COLLECT_DIRECT_DEPS);
-
setChildFlags();
}
@@ -723,7 +740,7 @@ private void collectConditionalDependencies()
}
final ConditionalDependency conditionalDep = new ConditionalDependency(conditionalInfo, this);
conditionalDepsToProcess.add(conditionalDep);
- conditionalDep.appDep.collectConditionalDependencies();
+ conditionalDep.conditionalDep.collectConditionalDependencies();
}
}
}
@@ -1019,7 +1036,7 @@ private boolean replaceDirectDepBranch(DependencyNode parentNode, boolean replac
private class ConditionalDependency {
- final AppDep appDep;
+ final AppDep conditionalDep;
private boolean activated;
private ConditionalDependency(ExtensionInfo info, AppDep parent) {
@@ -1030,12 +1047,12 @@ private ConditionalDependency(ExtensionInfo info, AppDep parent) {
new BootstrapArtifactVersion(info.runtimeArtifact.getVersion())));
rtNode.setRepositories(parent.ext.runtimeNode.getRepositories());
- appDep = new AppDep(parent, rtNode);
- appDep.ext = new ExtensionDependency(info, rtNode, parent.ext.exclusions);
+ conditionalDep = new AppDep(parent, rtNode);
+ conditionalDep.ext = new ExtensionDependency(info, rtNode, parent.ext.exclusions);
}
ExtensionDependency getExtensionDependency() {
- return appDep.ext;
+ return conditionalDep.ext;
}
void activate() {
@@ -1044,7 +1061,7 @@ void activate() {
}
activated = true;
final ExtensionDependency extDep = getExtensionDependency();
- final DependencyNode originalNode = collectDependencies(appDep.ext.info.runtimeArtifact, extDep.exclusions,
+ final DependencyNode originalNode = collectDependencies(conditionalDep.ext.info.runtimeArtifact, extDep.exclusions,
extDep.runtimeNode.getRepositories());
final DefaultDependencyNode rtNode = (DefaultDependencyNode) extDep.runtimeNode;
rtNode.setRepositories(originalNode.getRepositories());
@@ -1057,39 +1074,28 @@ void activate() {
currentChildren.addAll(originalNode.getChildren());
}
- appDep.walkingFlags = COLLECT_DIRECT_DEPS;
+ conditionalDep.walkingFlags = COLLECT_DIRECT_DEPS;
if (collectReloadableModules) {
- appDep.walkingFlags |= COLLECT_RELOADABLE_MODULES;
+ conditionalDep.walkingFlags |= COLLECT_RELOADABLE_MODULES;
}
- var futures = new ArrayList>();
- appDep.scheduleRuntimeVisit(futures);
- CompletableFuture.allOf(futures.toArray(new CompletableFuture>[0])).join();
- if (logErrors()) {
- throw new RuntimeException(
- "Failed to process Quarkus application conditional dependencies, please see the errors logged above for more details.");
- }
-
- appDep.setFlags(appDep.walkingFlags);
-
- var parentExtDep = appDep.parent;
- parentExtDep.children.add(appDep);
- while (parentExtDep != null) {
- if (parentExtDep.ext != null) {
- parentExtDep.ext.addExtensionDependency(appDep.ext);
- break;
- }
- parentExtDep = parentExtDep.parent;
+ var phaser = new Phaser(1);
+ conditionalDep.scheduleRuntimeVisit(phaser);
+ phaser.arriveAndAwaitAdvance();
+ assertNoErrors();
+ conditionalDep.setFlags(conditionalDep.walkingFlags);
+ if (conditionalDep.parent.resolvedDep == null) {
+ conditionalDep.parent.allDeps.add(conditionalDep.resolvedDep.getArtifactCoords());
+ } else {
+ conditionalDep.parent.resolvedDep.addDependency(conditionalDep.resolvedDep.getArtifactCoords());
}
- appDep.ext.info.ensureActivated();
-
- appDep.parent.ext.runtimeNode.getChildren().add(rtNode);
+ conditionalDep.parent.ext.runtimeNode.getChildren().add(rtNode);
}
boolean isSatisfied() {
- if (appDep.ext.info.dependencyCondition == null) {
+ if (conditionalDep.ext.info.dependencyCondition == null) {
return true;
}
- for (ArtifactKey key : appDep.ext.info.dependencyCondition) {
+ for (ArtifactKey key : conditionalDep.ext.info.dependencyCondition) {
if (!isRuntimeArtifact(key)) {
return false;
}
@@ -1125,19 +1131,18 @@ private class AppDepLogger {
private AppDepLogger() {
}
- void log(AppDep root) {
- logInternal(root);
-
- final int childrenTotal = root.children.size();
+ void log(AppDep dep) {
+ logInternal(dep);
+ final int childrenTotal = dep.node.getChildren().size();
if (childrenTotal > 0) {
if (childrenTotal == 1) {
depth.add(false);
- log(root.children.get(0));
+ log(dep.children.get(0));
} else {
depth.add(true);
int i = 0;
while (i < childrenTotal) {
- log(root.children.get(i++));
+ log(dep.children.get(i++));
if (i == childrenTotal - 1) {
depth.set(depth.size() - 1, false);
}
@@ -1166,25 +1171,26 @@ private void logInternal(AppDep dep) {
buf.append('\u2514').append('\u2500').append(' ');
}
}
- buf.append(dep.node.getArtifact());
+ var resolvedDep = getResolvedDependency(getKey(dep.node.getArtifact()));
+ buf.append(resolvedDep.toCompactCoords());
if (!depth.isEmpty()) {
- appendFlags(buf, getResolvedDependency(getKey(dep.node.getArtifact())));
+ appendFlags(buf, resolvedDep);
}
depLogging.getMessageConsumer().accept(buf.toString());
if (depLogging.isGraph()) {
- var depKeys = artifactDeps.get(getCoords(dep.node.getArtifact()));
- if (depKeys != null && !depKeys.isEmpty() && depKeys.size() != dep.children.size()) {
- final Map versions = new HashMap<>(dep.children.size());
+ var deps = resolvedDep.getDependencies();
+ if (!deps.isEmpty() && deps.size() != dep.children.size()) {
+ final Map versions = new HashMap<>(dep.children.size());
for (var c : dep.children) {
- versions.put(getKey(c.node.getArtifact()), c.node.getArtifact().getVersion());
+ versions.put(getCoords(c.node.getArtifact()), null);
}
- var list = new ArrayList(depKeys.size() - dep.children.size());
- for (var key : depKeys) {
- if (!versions.containsKey(key)) {
- var d = getResolvedDependency(key);
- var sb = new StringBuilder().append(d.toGACTVString());
- appendFlags(sb, d);
+ var list = new ArrayList(deps.size() - dep.children.size());
+ for (var coords : deps) {
+ if (!versions.containsKey(coords)) {
+ var childDep = getResolvedDependency(coords.getKey());
+ var sb = new StringBuilder().append(childDep.toCompactCoords());
+ appendFlags(sb, childDep);
list.add(sb.append(" [+]").toString());
}
}
@@ -1225,7 +1231,7 @@ private void logInternal(AppDep dep) {
}
}
- private void appendFlags(StringBuilder sb, ResolvedDependencyBuilder d) {
+ private void appendFlags(StringBuilder sb, ResolvedDependency d) {
sb.append(" (").append(d.getScope());
if (d.isFlagSet(DependencyFlags.OPTIONAL)) {
sb.append(" optional");
@@ -1247,11 +1253,14 @@ private void appendFlags(StringBuilder sb, ResolvedDependencyBuilder d) {
}
private ResolvedDependencyBuilder getResolvedDependency(ArtifactKey key) {
- var d = appBuilder.getDependency(key);
- if (d == null) {
- throw new IllegalArgumentException(key + " is not found among application dependencies");
+ var resolvedDep = appBuilder.getDependency(key);
+ if (resolvedDep == null) {
+ if (appBuilder.getApplicationArtifact().getKey().equals(key)) {
+ return appBuilder.getApplicationArtifact();
+ }
+ throw new IllegalArgumentException("Failed to locate " + key + " among application dependencies");
}
- return d;
+ return resolvedDep;
}
}
}
diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml
index 87bd3e65cc0fb..47c5123683b40 100644
--- a/independent-projects/bootstrap/pom.xml
+++ b/independent-projects/bootstrap/pom.xml
@@ -74,7 +74,7 @@
2.0
3.5.1
2.3.0
- 1.4.0
+ 1.4.1
8.6
0.0.10
0.1.3
diff --git a/integration-tests/cache/src/main/java/io/quarkus/it/cache/ExpensiveResource.java b/integration-tests/cache/src/main/java/io/quarkus/it/cache/ExpensiveResource.java
index 8efdcf9abfdc0..84cdc5a3a75bd 100644
--- a/integration-tests/cache/src/main/java/io/quarkus/it/cache/ExpensiveResource.java
+++ b/integration-tests/cache/src/main/java/io/quarkus/it/cache/ExpensiveResource.java
@@ -13,11 +13,13 @@
@Path("/expensive-resource")
public class ExpensiveResource {
+ public static final String EXPENSIVE_RESOURCE_CACHE_NAME = "expensiveResourceCache";
+
private int invocations;
@GET
@Path("/{keyElement1}/{keyElement2}/{keyElement3}")
- @CacheResult(cacheName = "expensiveResourceCache", lockTimeout = 5000)
+ @CacheResult(cacheName = EXPENSIVE_RESOURCE_CACHE_NAME, lockTimeout = 5000)
public ExpensiveResponse getExpensiveResponse(@PathParam("keyElement1") @CacheKey String keyElement1,
@PathParam("keyElement2") @CacheKey String keyElement2, @PathParam("keyElement3") @CacheKey String keyElement3,
@QueryParam("foo") String foo) {
diff --git a/integration-tests/cache/src/main/java/io/quarkus/it/cache/GetIfPresentResource.java b/integration-tests/cache/src/main/java/io/quarkus/it/cache/GetIfPresentResource.java
new file mode 100644
index 0000000000000..14e9feb8082c4
--- /dev/null
+++ b/integration-tests/cache/src/main/java/io/quarkus/it/cache/GetIfPresentResource.java
@@ -0,0 +1,36 @@
+package io.quarkus.it.cache;
+
+import static java.util.concurrent.CompletableFuture.completedFuture;
+
+import java.util.concurrent.CompletionStage;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+
+import org.jboss.resteasy.reactive.RestPath;
+
+import io.quarkus.cache.Cache;
+import io.quarkus.cache.CacheName;
+import io.quarkus.cache.CaffeineCache;
+
+@Path("/get-if-present")
+public class GetIfPresentResource {
+
+ public static final String GET_IF_PRESENT_CACHE_NAME = "getIfPresentCache";
+
+ @CacheName(GET_IF_PRESENT_CACHE_NAME)
+ Cache cache;
+
+ @GET
+ @Path("/{key}")
+ public CompletionStage getIfPresent(@RestPath String key) {
+ return cache.as(CaffeineCache.class).getIfPresent(key);
+ }
+
+ @PUT
+ @Path("/{key}")
+ public void put(@RestPath String key, String value) {
+ cache.as(CaffeineCache.class).put(key, completedFuture(value));
+ }
+}
diff --git a/integration-tests/cache/src/main/resources/application.properties b/integration-tests/cache/src/main/resources/application.properties
index b94edbb983678..9f87c19434340 100644
--- a/integration-tests/cache/src/main/resources/application.properties
+++ b/integration-tests/cache/src/main/resources/application.properties
@@ -8,5 +8,6 @@ quarkus.cache.caffeine."forest".expire-after-write=10M
quarkus.cache.caffeine."expensiveResourceCache".expire-after-write=10M
quarkus.cache.caffeine."expensiveResourceCache".metrics-enabled=true
+quarkus.cache.caffeine."getIfPresentCache".metrics-enabled=true
io.quarkus.it.cache.SunriseRestClient/mp-rest/url=${test.url}
diff --git a/integration-tests/cache/src/test/java/io/quarkus/it/cache/CacheTestCase.java b/integration-tests/cache/src/test/java/io/quarkus/it/cache/CacheTestCase.java
index 48fc07d224685..64197281e8775 100644
--- a/integration-tests/cache/src/test/java/io/quarkus/it/cache/CacheTestCase.java
+++ b/integration-tests/cache/src/test/java/io/quarkus/it/cache/CacheTestCase.java
@@ -1,5 +1,8 @@
package io.quarkus.it.cache;
+import static io.quarkus.it.cache.ExpensiveResource.EXPENSIVE_RESOURCE_CACHE_NAME;
+import static io.quarkus.it.cache.GetIfPresentResource.GET_IF_PRESENT_CACHE_NAME;
+import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.when;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -14,20 +17,65 @@
public class CacheTestCase {
@Test
- public void testCache() {
+ void testCache() {
+ assertMetrics(EXPENSIVE_RESOURCE_CACHE_NAME, 0, 0, 0);
+
runExpensiveRequest();
+ assertMetrics(EXPENSIVE_RESOURCE_CACHE_NAME, 1, 1, 0);
+
runExpensiveRequest();
+ assertMetrics(EXPENSIVE_RESOURCE_CACHE_NAME, 1, 1, 1);
+
runExpensiveRequest();
- when().get("/expensive-resource/invocations").then().statusCode(200).body(is("1"));
+ assertMetrics(EXPENSIVE_RESOURCE_CACHE_NAME, 1, 1, 2);
- String metricsResponse = when().get("/q/metrics").then().extract().asString();
- assertTrue(metricsResponse.contains("cache_puts_total{cache=\"expensiveResourceCache\"} 1.0"));
- assertTrue(metricsResponse.contains("cache_gets_total{cache=\"expensiveResourceCache\",result=\"miss\"} 1.0"));
- assertTrue(metricsResponse.contains("cache_gets_total{cache=\"expensiveResourceCache\",result=\"hit\"} 2.0"));
+ when().get("/expensive-resource/invocations").then().statusCode(200).body(is("1"));
}
private void runExpensiveRequest() {
when().get("/expensive-resource/I/love/Quarkus?foo=bar").then().statusCode(200).body("result",
is("I love Quarkus too!"));
}
+
+ @Test
+ void testGetIfPresentMetrics() {
+ assertMetrics(GET_IF_PRESENT_CACHE_NAME, 0, 0, 0);
+
+ String cacheKey = "foo";
+ String cacheValue = "bar";
+
+ given().pathParam("key", cacheKey)
+ .when().get("/get-if-present/{key}")
+ .then().statusCode(204);
+ assertMetrics(GET_IF_PRESENT_CACHE_NAME, 0, 1, 0);
+
+ given().pathParam("key", cacheKey)
+ .when().get("/get-if-present/{key}")
+ .then().statusCode(204);
+ assertMetrics(GET_IF_PRESENT_CACHE_NAME, 0, 2, 0);
+
+ given().pathParam("key", cacheKey).body(cacheValue)
+ .when().put("/get-if-present/{key}")
+ .then().statusCode(204);
+ assertMetrics(GET_IF_PRESENT_CACHE_NAME, 1, 2, 0);
+
+ given().pathParam("key", cacheKey)
+ .when().get("/get-if-present/{key}")
+ .then().statusCode(200).body(is(cacheValue));
+ assertMetrics(GET_IF_PRESENT_CACHE_NAME, 1, 2, 1);
+
+ given().pathParam("key", cacheKey)
+ .when().get("/get-if-present/{key}")
+ .then().statusCode(200).body(is(cacheValue));
+ assertMetrics(GET_IF_PRESENT_CACHE_NAME, 1, 2, 2);
+ }
+
+ private void assertMetrics(String cacheName, double expectedPuts, double expectedMisses, double expectedHits) {
+ String metricsResponse = when().get("/q/metrics").then().extract().asString();
+ assertTrue(metricsResponse.contains(String.format("cache_puts_total{cache=\"%s\"} %.1f", cacheName, expectedPuts)));
+ assertTrue(metricsResponse
+ .contains(String.format("cache_gets_total{cache=\"%s\",result=\"miss\"} %.1f", cacheName, expectedMisses)));
+ assertTrue(metricsResponse
+ .contains(String.format("cache_gets_total{cache=\"%s\",result=\"hit\"} %.1f", cacheName, expectedHits)));
+ }
}
diff --git a/integration-tests/infinispan-cache/pom.xml b/integration-tests/infinispan-cache/pom.xml
new file mode 100644
index 0000000000000..670df99e995ea
--- /dev/null
+++ b/integration-tests/infinispan-cache/pom.xml
@@ -0,0 +1,144 @@
+
+
+ 4.0.0
+
+ io.quarkus
+ quarkus-integration-tests-parent
+ 999-SNAPSHOT
+ ../pom.xml
+
+
+ quarkus-integration-test-infinispan-cache
+ Quarkus - Integration Tests - Infinispan Cache
+
+
+
+ io.quarkus
+ quarkus-rest-jackson
+
+
+ io.quarkus
+ quarkus-rest-client-jackson
+
+
+ io.quarkus
+ quarkus-infinispan-cache
+
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+
+
+ io.quarkus
+ quarkus-infinispan-cache-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-rest-jackson-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-rest-client-jackson-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ maven-surefire-plugin
+
+ true
+
+
+
+ maven-failsafe-plugin
+
+ true
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ build
+
+
+
+
+
+
+
+
+
+
+ test-infinispan
+
+
+ test-containers
+
+
+
+
+
+ maven-surefire-plugin
+
+ false
+
+
+
+ maven-failsafe-plugin
+
+ false
+
+
+
+
+
+
+
+
diff --git a/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/ExpensiveResource.java b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/ExpensiveResource.java
new file mode 100644
index 0000000000000..326c01a3c7b45
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/ExpensiveResource.java
@@ -0,0 +1,53 @@
+package io.quarkus.it.cache.infinispan;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.QueryParam;
+
+import org.infinispan.protostream.GeneratedSchema;
+import org.infinispan.protostream.annotations.Proto;
+import org.infinispan.protostream.annotations.ProtoSchema;
+
+import io.quarkus.cache.CacheInvalidateAll;
+import io.quarkus.cache.CacheKey;
+import io.quarkus.cache.CacheResult;
+
+@Path("/expensive-resource")
+public class ExpensiveResource {
+
+ private final AtomicInteger invocations = new AtomicInteger(0);
+
+ @GET
+ @Path("/{keyElement1}/{keyElement2}/{keyElement3}")
+ @CacheResult(cacheName = "expensiveResourceCache")
+ public ExpensiveResponse getExpensiveResponse(@PathParam("keyElement1") @CacheKey String keyElement1,
+ @PathParam("keyElement2") @CacheKey String keyElement2, @PathParam("keyElement3") @CacheKey String keyElement3,
+ @QueryParam("foo") String foo) {
+ invocations.incrementAndGet();
+ return new ExpensiveResponse(keyElement1 + " " + keyElement2 + " " + keyElement3 + " too!");
+ }
+
+ @POST
+ @CacheInvalidateAll(cacheName = "expensiveResourceCache")
+ public void invalidateAll() {
+
+ }
+
+ @GET
+ @Path("/invocations")
+ public int getInvocations() {
+ return invocations.get();
+ }
+
+ @Proto
+ public record ExpensiveResponse(String result) {
+ }
+
+ @ProtoSchema(includeClasses = { ExpensiveResponse.class })
+ interface Schema extends GeneratedSchema {
+ }
+}
diff --git a/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/RestClientResource.java b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/RestClientResource.java
new file mode 100644
index 0000000000000..09d8ea5978660
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/RestClientResource.java
@@ -0,0 +1,88 @@
+package io.quarkus.it.cache.infinispan;
+
+import java.util.Set;
+import java.util.function.Function;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.HttpHeaders;
+
+import org.eclipse.microprofile.rest.client.inject.RestClient;
+import org.jboss.resteasy.reactive.RestPath;
+import org.jboss.resteasy.reactive.RestQuery;
+import org.jboss.resteasy.reactive.RestResponse;
+
+import io.quarkus.runtime.BlockingOperationControl;
+import io.smallrye.mutiny.Uni;
+
+@Path("rest-client")
+public class RestClientResource {
+
+ @RestClient
+ SunriseRestClient sunriseRestClient;
+
+ @Inject
+ HttpHeaders headers; // used in order to make sure that @RequestScoped beans continue to work despite the cache coming into play
+
+ @GET
+ @Path("time/{city}")
+ public RestResponse getSunriseTime(@RestPath String city, @RestQuery String date) {
+ Set incomingHeadersBeforeRestCall = headers.getRequestHeaders().keySet();
+ String restResponse = sunriseRestClient.getSunriseTime(city, date);
+ Set incomingHeadersAfterRestCall = headers.getRequestHeaders().keySet();
+ return RestResponse.ResponseBuilder
+ .ok(restResponse)
+ .header("before", String.join(", ", incomingHeadersBeforeRestCall))
+ .header("after", String.join(", ", incomingHeadersAfterRestCall))
+ .header("blockingAllowed", BlockingOperationControl.isBlockingAllowed())
+ .build();
+ }
+
+ @GET
+ @Path("async/time/{city}")
+ public Uni> getAsyncSunriseTime(@RestPath String city, @RestQuery String date) {
+ Set incomingHeadersBeforeRestCall = headers.getRequestHeaders().keySet();
+ return sunriseRestClient.getAsyncSunriseTime(city, date).onItem().transform(new Function<>() {
+ @Override
+ public RestResponse apply(String restResponse) {
+ Set incomingHeadersAfterRestCall = headers.getRequestHeaders().keySet();
+ return RestResponse.ResponseBuilder
+ .ok(restResponse)
+ .header("before", String.join(", ", incomingHeadersBeforeRestCall))
+ .header("after", String.join(", ", incomingHeadersAfterRestCall))
+ .header("blockingAllowed", BlockingOperationControl.isBlockingAllowed())
+ .build();
+ }
+ });
+ }
+
+ @GET
+ @Path("invocations")
+ public Integer getSunriseTimeInvocations() {
+ return sunriseRestClient.getSunriseTimeInvocations();
+ }
+
+ @DELETE
+ @Path("invalidate/{city}")
+ public Uni> invalidate(@RestPath String city, @RestQuery String notPartOfTheCacheKey,
+ @RestQuery String date) {
+ return sunriseRestClient.invalidate(city, notPartOfTheCacheKey, date).onItem().transform(
+ new Function<>() {
+ @Override
+ public RestResponse apply(Void unused) {
+ return RestResponse.ResponseBuilder. create(RestResponse.Status.NO_CONTENT)
+ .header("blockingAllowed", BlockingOperationControl.isBlockingAllowed())
+ .header("incoming", String.join(", ", headers.getRequestHeaders().keySet()))
+ .build();
+ }
+ });
+ }
+
+ @DELETE
+ @Path("invalidate")
+ public void invalidateAll() {
+ sunriseRestClient.invalidateAll();
+ }
+}
diff --git a/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/SunriseRestClient.java b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/SunriseRestClient.java
new file mode 100644
index 0000000000000..f8ee388c9c2ee
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/SunriseRestClient.java
@@ -0,0 +1,52 @@
+package io.quarkus.it.cache.infinispan;
+
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+
+import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
+import org.jboss.resteasy.reactive.RestPath;
+import org.jboss.resteasy.reactive.RestQuery;
+
+import io.quarkus.cache.CacheInvalidate;
+import io.quarkus.cache.CacheInvalidateAll;
+import io.quarkus.cache.CacheKey;
+import io.quarkus.cache.CacheResult;
+import io.smallrye.mutiny.Uni;
+
+@RegisterRestClient
+@Path("sunrise")
+public interface SunriseRestClient {
+
+ String CACHE_NAME = "sunrise-cache";
+
+ @GET
+ @Path("time/{city}")
+ @CacheResult(cacheName = CACHE_NAME)
+ String getSunriseTime(@RestPath String city, @RestQuery String date);
+
+ @GET
+ @Path("time/{city}")
+ @CacheResult(cacheName = CACHE_NAME)
+ Uni getAsyncSunriseTime(@RestPath String city, @RestQuery String date);
+
+ @GET
+ @Path("invocations")
+ Integer getSunriseTimeInvocations();
+
+ /*
+ * The following methods wouldn't make sense in a real-life application but it's not relevant here. We only need to check if
+ * the caching annotations work as intended with the rest-client extension.
+ */
+
+ @DELETE
+ @Path("invalidate/{city}")
+ @CacheInvalidate(cacheName = CACHE_NAME)
+ Uni invalidate(@CacheKey @RestPath String city, @RestQuery String notPartOfTheCacheKey,
+ @CacheKey @RestPath String date);
+
+ @DELETE
+ @Path("invalidate")
+ @CacheInvalidateAll(cacheName = CACHE_NAME)
+ void invalidateAll();
+}
diff --git a/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/SunriseRestServerResource.java b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/SunriseRestServerResource.java
new file mode 100644
index 0000000000000..fb21d7c9e7e91
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/main/java/io/quarkus/it/cache/infinispan/SunriseRestServerResource.java
@@ -0,0 +1,41 @@
+package io.quarkus.it.cache.infinispan;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+
+import org.jboss.resteasy.reactive.RestPath;
+import org.jboss.resteasy.reactive.RestQuery;
+
+@ApplicationScoped
+@Path("sunrise")
+public class SunriseRestServerResource {
+
+ private int sunriseTimeInvocations;
+
+ @GET
+ @Path("time/{city}")
+ public String getSunriseTime(@RestPath String city, @RestQuery String date) {
+ sunriseTimeInvocations++;
+ return "2020-12-20T10:15:30";
+ }
+
+ @GET
+ @Path("invocations")
+ public Integer getSunriseTimeInvocations() {
+ return sunriseTimeInvocations;
+ }
+
+ @DELETE
+ @Path("invalidate/{city}")
+ public void invalidate(@RestPath String city, @RestQuery String notPartOfTheCacheKey, @RestQuery String date) {
+ // Do nothing. We only need to test the caching annotation on the client side.
+ }
+
+ @DELETE
+ @Path("invalidate")
+ public void invalidateAll() {
+ // Do nothing. We only need to test the caching annotation on the client side.
+ }
+}
diff --git a/integration-tests/infinispan-cache/src/main/resources/application.properties b/integration-tests/infinispan-cache/src/main/resources/application.properties
new file mode 100644
index 0000000000000..25525ca3c8e4f
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/main/resources/application.properties
@@ -0,0 +1 @@
+io.quarkus.it.cache.infinispan.SunriseRestClient/mp-rest/url=${test.url}
diff --git a/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/CacheIT.java b/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/CacheIT.java
new file mode 100644
index 0000000000000..de4fb795de42b
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/CacheIT.java
@@ -0,0 +1,7 @@
+package io.quarkus.it.cache.infinispan;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+public class CacheIT extends CacheTest {
+}
diff --git a/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/CacheTest.java b/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/CacheTest.java
new file mode 100644
index 0000000000000..b5a73d4558a6f
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/CacheTest.java
@@ -0,0 +1,33 @@
+package io.quarkus.it.cache.infinispan;
+
+import static io.restassured.RestAssured.when;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+public class CacheTest {
+
+ @Test
+ public void testCache() {
+ runExpensiveRequest();
+ runExpensiveRequest();
+ runExpensiveRequest();
+ when().get("/expensive-resource/invocations").then().statusCode(200).body(is("1"));
+
+ when()
+ .post("/expensive-resource")
+ .then()
+ .statusCode(204);
+ }
+
+ private void runExpensiveRequest() {
+ when()
+ .get("/expensive-resource/I/love/Quarkus?foo=bar")
+ .then()
+ .statusCode(200)
+ .body("result", is("I love Quarkus too!"));
+ }
+}
diff --git a/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/InfinspanCacheClientTestCase.java b/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/InfinspanCacheClientTestCase.java
new file mode 100644
index 0000000000000..ccfa3c8dd5fc8
--- /dev/null
+++ b/integration-tests/infinispan-cache/src/test/java/io/quarkus/it/cache/infinispan/InfinspanCacheClientTestCase.java
@@ -0,0 +1,87 @@
+package io.quarkus.it.cache.infinispan;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.http.Headers;
+
+@QuarkusTest
+@DisplayName("Tests the integration between the infinispan cache and the rest-client extensions")
+public class InfinspanCacheClientTestCase {
+
+ private static final String CITY = "Toulouse";
+ private static final String TODAY = "2020-12-20";
+
+ @Test
+ public void test() {
+ assertInvocations("0");
+ getSunriseTimeInvocations();
+ assertInvocations("1");
+ getSunriseTimeInvocations();
+ assertInvocations("1");
+ getAsyncSunriseTimeInvocations();
+ assertInvocations("1");
+ invalidate();
+ getSunriseTimeInvocations();
+ assertInvocations("2");
+ invalidateAll();
+ getSunriseTimeInvocations();
+ assertInvocations("3");
+ }
+
+ private void assertInvocations(String expectedInvocations) {
+ given()
+ .when()
+ .get("/rest-client/invocations")
+ .then()
+ .statusCode(200)
+ .body(equalTo(expectedInvocations));
+ }
+
+ private void getSunriseTimeInvocations() {
+ doGetSunriseTimeInvocations("/rest-client/time/{city}", true);
+ }
+
+ private void getAsyncSunriseTimeInvocations() {
+ doGetSunriseTimeInvocations("/rest-client/async/time/{city}", false);
+ }
+
+ private void doGetSunriseTimeInvocations(String path, Boolean blockingAllowed) {
+ Headers headers = given()
+ .queryParam("date", TODAY)
+ .when()
+ .get(path, CITY)
+ .then()
+ .statusCode(200)
+ .extract().headers();
+ assertEquals(headers.get("before").getValue(), headers.get("after").getValue());
+ assertEquals(blockingAllowed.toString(), headers.get("blockingAllowed").getValue());
+ }
+
+ private void invalidate() {
+ Headers headers = given()
+ .queryParam("date", TODAY)
+ .queryParam("notPartOfTheCacheKey", "notPartOfTheCacheKey")
+ .when()
+ .delete("/rest-client/invalidate/{city}", CITY)
+ .then()
+ .statusCode(204)
+ .extract().headers();
+ assertNotNull(headers.get("incoming").getValue());
+ assertEquals("false", headers.get("blockingAllowed").getValue());
+ }
+
+ private void invalidateAll() {
+ given()
+ .when()
+ .delete("/rest-client/invalidate")
+ .then()
+ .statusCode(204);
+ }
+}
diff --git a/integration-tests/kafka-oauth-keycloak/pom.xml b/integration-tests/kafka-oauth-keycloak/pom.xml
index 91314691c89ca..43f19d3917acf 100644
--- a/integration-tests/kafka-oauth-keycloak/pom.xml
+++ b/integration-tests/kafka-oauth-keycloak/pom.xml
@@ -44,11 +44,6 @@
io.strimzi
kafka-oauth-client
-
-
- io.strimzi
- kafka-oauth-common
-
diff --git a/integration-tests/observability-lgtm/pom.xml b/integration-tests/observability-lgtm/pom.xml
new file mode 100644
index 0000000000000..0abc8789677cb
--- /dev/null
+++ b/integration-tests/observability-lgtm/pom.xml
@@ -0,0 +1,121 @@
+
+
+ 4.0.0
+
+
+ quarkus-integration-tests-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+
+ quarkus-integration-test-observability-lgtm
+ Quarkus - Integration Tests - Observability LGTM
+
+
+
+ io.quarkus
+ quarkus-observability-devservices-lgtm
+
+
+ io.quarkus
+ quarkus-rest
+
+
+
+ io.quarkiverse.micrometer.registry
+ quarkus-micrometer-registry-otlp
+ 3.2.4
+
+
+ io.quarkus
+ quarkus-micrometer
+
+
+ io.quarkus
+ quarkus-opentelemetry
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+ io.quarkus
+ quarkus-rest-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-opentelemetry-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-micrometer-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ build
+
+
+
+
+
+
+
diff --git a/integration-tests/observability-lgtm/src/main/java/io/quarkus/observability/example/SimpleEndpoint.java b/integration-tests/observability-lgtm/src/main/java/io/quarkus/observability/example/SimpleEndpoint.java
new file mode 100644
index 0000000000000..17684efd1e944
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/main/java/io/quarkus/observability/example/SimpleEndpoint.java
@@ -0,0 +1,48 @@
+package io.quarkus.observability.example;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+
+import org.jboss.logging.Logger;
+
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+
+@Path("/api")
+public class SimpleEndpoint {
+ private static final Logger log = Logger.getLogger(SimpleEndpoint.class);
+
+ @Inject
+ MeterRegistry registry;
+
+ Random random = new SecureRandom();
+ double[] arr = new double[1];
+
+ @PostConstruct
+ public void start() {
+ String key = System.getProperty("tag-key", "test");
+ Gauge.builder("xvalue", arr, a -> arr[0])
+ .baseUnit("X")
+ .description("Some random x")
+ .tag(key, "x")
+ .register(registry);
+ }
+
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ @Path("/poke")
+ public String poke(@QueryParam("f") int f) {
+ log.infof("Poke %s", f);
+ double x = random.nextDouble() * f;
+ arr[0] = x;
+ return "poke:" + x;
+ }
+}
diff --git a/integration-tests/observability-lgtm/src/main/resources/application.properties b/integration-tests/observability-lgtm/src/main/resources/application.properties
new file mode 100644
index 0000000000000..b88a2d3f9bb17
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/main/resources/application.properties
@@ -0,0 +1,17 @@
+quarkus.log.category."io.quarkus.observability".level=DEBUG
+quarkus.log.category."io.quarkus.devservices".level=DEBUG
+
+#micrometer
+quarkus.micrometer.export.otlp.enabled=true
+quarkus.micrometer.export.otlp.publish=true
+quarkus.micrometer.export.otlp.step=PT5S
+quarkus.micrometer.export.otlp.default-registry=true
+%dev.quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics
+%prod.quarkus.micrometer.export.otlp.url=http://localhost:4318/v1/metrics
+
+#opentelemetry
+quarkus.otel.exporter.otlp.traces.protocol=http/protobuf
+%dev.quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url}
+%prod.quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4318
+
+#quarkus.observability.lgtm.image-name=grafana/otel-lgtm
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmLifecycleTest.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmLifecycleTest.java
new file mode 100644
index 0000000000000..8660c82c95b93
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmLifecycleTest.java
@@ -0,0 +1,17 @@
+package io.quarkus.observability.test;
+
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import io.quarkus.observability.devresource.lgtm.LgtmResource;
+import io.quarkus.observability.test.support.QuarkusTestResourceTestProfile;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+
+@QuarkusTest
+@QuarkusTestResource(value = LgtmResource.class, restrictToAnnotatedClass = true)
+@TestProfile(QuarkusTestResourceTestProfile.class)
+@DisabledOnOs(OS.WINDOWS)
+public class LgtmLifecycleTest extends LgtmTestBase {
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesIT.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesIT.java
new file mode 100644
index 0000000000000..fe869cf62b010
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesIT.java
@@ -0,0 +1,11 @@
+package io.quarkus.observability.test;
+
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+@DisabledOnOs(OS.WINDOWS)
+public class LgtmResourcesIT extends LgtmResourcesTest {
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesTest.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesTest.java
new file mode 100644
index 0000000000000..6cd232235b38f
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesTest.java
@@ -0,0 +1,14 @@
+package io.quarkus.observability.test;
+
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import io.quarkus.observability.test.support.DevResourcesTestProfile;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+
+@QuarkusTest
+@TestProfile(DevResourcesTestProfile.class)
+@DisabledOnOs(OS.WINDOWS)
+public class LgtmResourcesTest extends LgtmTestBase {
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmServicesTest.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmServicesTest.java
new file mode 100644
index 0000000000000..33b7e0c13da7c
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmServicesTest.java
@@ -0,0 +1,11 @@
+package io.quarkus.observability.test;
+
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+@DisabledOnOs(OS.WINDOWS)
+public class LgtmServicesTest extends LgtmTestBase {
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java
new file mode 100644
index 0000000000000..6223f25763584
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java
@@ -0,0 +1,33 @@
+package io.quarkus.observability.test;
+
+import java.util.concurrent.TimeUnit;
+
+import org.awaitility.Awaitility;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.jboss.logging.Logger;
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.observability.test.support.GrafanaClient;
+import io.restassured.RestAssured;
+
+public abstract class LgtmTestBase {
+ private final Logger log = Logger.getLogger(getClass());
+
+ @ConfigProperty(name = "quarkus.grafana.url")
+ String url;
+
+ @Test
+ public void testTracing() {
+ log.info("Testing Grafana ...");
+ String response = RestAssured.get("/api/poke?f=100").body().asString();
+ log.info("Response: " + response);
+ GrafanaClient client = new GrafanaClient("http://" + url, "admin", "admin");
+ Awaitility.await().atMost(61, TimeUnit.SECONDS).until(
+ client::user,
+ u -> "admin".equals(u.login));
+ Awaitility.await().atMost(61, TimeUnit.SECONDS).until(
+ () -> client.query("xvalue_X"),
+ result -> !result.data.result.isEmpty());
+ }
+
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/DevResourcesTestProfile.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/DevResourcesTestProfile.java
new file mode 100644
index 0000000000000..1110a11073098
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/DevResourcesTestProfile.java
@@ -0,0 +1,14 @@
+package io.quarkus.observability.test.support;
+
+import java.util.Map;
+
+import io.quarkus.test.junit.QuarkusTestProfile;
+
+public class DevResourcesTestProfile implements QuarkusTestProfile {
+ @Override
+ public Map getConfigOverrides() {
+ return Map.of(
+ "quarkus.observability.dev-resources", "true",
+ "quarkus.observability.enabled", "false");
+ }
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java
new file mode 100644
index 0000000000000..40f31cc689589
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java
@@ -0,0 +1,92 @@
+package io.quarkus.observability.test.support;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.Base64;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class GrafanaClient {
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private final String url;
+ private final String username;
+ private final String password;
+
+ public GrafanaClient(String url, String username, String password) {
+ this.url = url;
+ this.username = username;
+ this.password = password;
+ }
+
+ private void handle(
+ String path,
+ Function method,
+ HttpResponse.BodyHandler bodyHandler,
+ BiConsumer, T> consumer) {
+ try {
+ String credentials = username + ":" + password;
+ String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
+
+ HttpClient httpClient = HttpClient.newHttpClient();
+ HttpRequest.Builder builder = HttpRequest.newBuilder()
+ .uri(URI.create(url + path))
+ .header("Authorization", "Basic " + encodedCredentials);
+ HttpRequest request = method.apply(builder).build();
+
+ HttpResponse response = httpClient.send(request, bodyHandler);
+ int code = response.statusCode();
+ if (code < 200 || code > 299) {
+ throw new IllegalStateException("Bad response: " + code + " >> " + response.body());
+ }
+ consumer.accept(response, response.body());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ }
+
+ public User user() {
+ AtomicReference ref = new AtomicReference<>();
+ handle(
+ "/api/user",
+ HttpRequest.Builder::GET,
+ HttpResponse.BodyHandlers.ofString(),
+ (r, b) -> {
+ try {
+ User user = MAPPER.readValue(b, User.class);
+ ref.set(user);
+ } catch (JsonProcessingException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ return ref.get();
+ }
+
+ public QueryResult query(String query) {
+ AtomicReference ref = new AtomicReference<>();
+ handle(
+ "/api/datasources/proxy/1/api/v1/query?query=" + query,
+ HttpRequest.Builder::GET,
+ HttpResponse.BodyHandlers.ofString(),
+ (r, b) -> {
+ try {
+ QueryResult result = MAPPER.readValue(b, QueryResult.class);
+ ref.set(result);
+ } catch (JsonProcessingException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ return ref.get();
+ }
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QuarkusTestResourceTestProfile.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QuarkusTestResourceTestProfile.java
new file mode 100644
index 0000000000000..b60772d61d550
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QuarkusTestResourceTestProfile.java
@@ -0,0 +1,14 @@
+package io.quarkus.observability.test.support;
+
+import java.util.Map;
+
+import io.quarkus.test.junit.QuarkusTestProfile;
+
+public class QuarkusTestResourceTestProfile implements QuarkusTestProfile {
+ @Override
+ public Map getConfigOverrides() {
+ return Map.of(
+ "quarkus.observability.dev-resources", "false",
+ "quarkus.observability.enabled", "false");
+ }
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QueryResult.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QueryResult.java
new file mode 100644
index 0000000000000..77f24fa7e5a26
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QueryResult.java
@@ -0,0 +1,73 @@
+package io.quarkus.observability.test.support;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class QueryResult {
+ public String status;
+ public Data data;
+
+ // getters and setters
+
+ @Override
+ public String toString() {
+ return "QueryResult{" +
+ "status='" + status + '\'' +
+ ", data=" + data +
+ '}';
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Data {
+ public String resultType;
+ public List result;
+
+ // getters and setters
+
+ @Override
+ public String toString() {
+ return "Data{" +
+ "resultType='" + resultType + '\'' +
+ ", result=" + result +
+ '}';
+ }
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ResultItem {
+ public Metric metric;
+ public List value;
+
+ // getters and setters
+
+ @Override
+ public String toString() {
+ return "ResultItem{" +
+ "metric=" + metric +
+ ", value=" + value +
+ '}';
+ }
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Metric {
+ @JsonProperty("__name__")
+ public String name;
+ public String job;
+ public String test;
+
+ // getters and setters
+
+ @Override
+ public String toString() {
+ return "Metric{" +
+ "name='" + name + '\'' +
+ ", job='" + job + '\'' +
+ ", test='" + test + '\'' +
+ '}';
+ }
+ }
+}
diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/User.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/User.java
new file mode 100644
index 0000000000000..f617cd2b23bcf
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/User.java
@@ -0,0 +1,14 @@
+package io.quarkus.observability.test.support;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class User {
+ @JsonProperty
+ public int id;
+ @JsonProperty
+ public String email;
+ @JsonProperty
+ public String login;
+}
diff --git a/integration-tests/observability-lgtm/src/test/resources/application.properties b/integration-tests/observability-lgtm/src/test/resources/application.properties
new file mode 100644
index 0000000000000..d3f0cfca1f442
--- /dev/null
+++ b/integration-tests/observability-lgtm/src/test/resources/application.properties
@@ -0,0 +1,16 @@
+# Disable default binders
+quarkus.micrometer.binder-enabled-default=false
+
+quarkus.log.category."io.quarkus.observability".level=DEBUG
+quarkus.log.category."io.quarkus.devservices".level=DEBUG
+
+#micrometer
+quarkus.micrometer.export.otlp.enabled=true
+quarkus.micrometer.export.otlp.publish=true
+quarkus.micrometer.export.otlp.step=PT5S
+quarkus.micrometer.export.otlp.default-registry=true
+quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics
+
+#opentelemetry
+quarkus.otel.exporter.otlp.traces.protocol=http/protobuf
+quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index e980289db1ccf..84544a0a183a4 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -177,6 +177,7 @@
hibernate-validator-resteasy-reactive
common-jpa-entities
infinispan-client
+ infinispan-cache
devtools
devtools-registry-client
@@ -246,6 +247,7 @@
flyway
liquibase
liquibase-mongodb
+ observability-lgtm
oidc
oidc-client
oidc-client-reactive
diff --git a/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/CountryResourceTest.java b/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/CountryResourceTest.java
index c3e5c3dafb9bf..3f77ae0ad5871 100644
--- a/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/CountryResourceTest.java
+++ b/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/CountryResourceTest.java
@@ -80,7 +80,7 @@ void testGetOne() {
.body(containsString("Greece"));
when().get("/country/getOne/100").then()
- .statusCode(500);
+ .statusCode(400);
}
@Test
diff --git a/pom.xml b/pom.xml
index a6156b23d4f24..24e5481ba443b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,11 +68,11 @@
5.4.0
- 1.62.2
+ 1.63.0
1.2.1
3.25.0
${protoc.version}
- 2.36.0
+ 2.37.1
7.4.0
diff --git a/relocations/generaterelocations.java b/relocations/generaterelocations.java
index 8c8042e5b01f1..cce239e9ff322 100755
--- a/relocations/generaterelocations.java
+++ b/relocations/generaterelocations.java
@@ -144,6 +144,11 @@ public class generaterelocations implements Runnable {
RELOCATIONS.put("quarkus-smallrye-reactive-messaging-mqtt-deployment", smallryeReactiveMessagingRelocation);
RELOCATIONS.put("quarkus-smallrye-reactive-messaging-rabbitmq", smallryeReactiveMessagingRelocation);
RELOCATIONS.put("quarkus-smallrye-reactive-messaging-rabbitmq-deployment", smallryeReactiveMessagingRelocation);
+
+ Function webjarsLocatorRelocation = a -> Relocation.ofArtifactId(a, a.replace("webjars-locator", "web-dependency-locator"),
+ "3.10");
+ RELOCATIONS.put("quarkus-webjars-locator", webjarsLocatorRelocation);
+ RELOCATIONS.put("quarkus-webjars-locator-deployment", webjarsLocatorRelocation);
}
private static final String RELOCATION_POM_TEMPLATE = "\n" + //
diff --git a/relocations/pom.xml b/relocations/pom.xml
index 5f147f47c930a..74c679ecd5ea4 100644
--- a/relocations/pom.xml
+++ b/relocations/pom.xml
@@ -100,6 +100,8 @@
quarkus-smallrye-reactive-messaging-pulsar-deployment
quarkus-smallrye-reactive-messaging-rabbitmq
quarkus-smallrye-reactive-messaging-rabbitmq-deployment
+ quarkus-webjars-locator
+ quarkus-webjars-locator-deployment
diff --git a/relocations/quarkus-webjars-locator-deployment/pom.xml b/relocations/quarkus-webjars-locator-deployment/pom.xml
new file mode 100644
index 0000000000000..a555a5b09851b
--- /dev/null
+++ b/relocations/quarkus-webjars-locator-deployment/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ quarkus-relocations-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-webjars-locator-deployment
+
+
+
+ io.quarkus
+ quarkus-web-dependency-locator-deployment
+ ${project.version}
+ Update the artifactId in your project build file. Refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.10 for more information.
+
+
+
\ No newline at end of file
diff --git a/relocations/quarkus-webjars-locator/pom.xml b/relocations/quarkus-webjars-locator/pom.xml
new file mode 100644
index 0000000000000..1985d8b5e21ea
--- /dev/null
+++ b/relocations/quarkus-webjars-locator/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ quarkus-relocations-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-webjars-locator
+
+
+
+ io.quarkus
+ quarkus-web-dependency-locator
+ ${project.version}
+ Update the artifactId in your project build file. Refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.10 for more information.
+
+
+
\ No newline at end of file