Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Dev UI and other Dev Mode classes from prod #44250

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.quarkus.deployment.builditem;

import java.util.Set;
import java.util.stream.Collectors;

import io.quarkus.builder.item.MultiBuildItem;

/**
* This allows you to define one or more packages or set of classes that should be removed on Prod Build
*/
public final class DevModeCleanupBuildItem extends MultiBuildItem {
private final Set<Class> classes;
private final Set<String> packageNames;

public DevModeCleanupBuildItem(Class c) {
this(c, false);
}

public DevModeCleanupBuildItem(Class c, boolean wholePackage) {
this(Set.of(c), wholePackage);
}

public DevModeCleanupBuildItem(Set<Class> c, boolean wholePackage) {
super();
if (wholePackage) {
this.classes = null;
this.packageNames = c.stream()
.map(clazz -> clazz.getPackage().getName())
.collect(Collectors.toSet());
} else {
this.classes = c;
this.packageNames = null;
}
}

public DevModeCleanupBuildItem(Set<Class> classes) {
super();
this.classes = classes;
this.packageNames = null;
}

public DevModeCleanupBuildItem(String... packageName) {
super();
this.packageNames = Set.of(packageName);
this.classes = null;

}

public Set<String> getPackageNames() {
return packageNames;
}

public Set<Class> getClasses() {
return classes;
}

public boolean isWholePackage() {
return this.classes == null;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.deployment.builditem;

import java.nio.file.Path;
import java.util.Set;

import io.quarkus.builder.item.MultiBuildItem;
Expand All @@ -11,12 +12,14 @@
*/
public final class RemovedResourceBuildItem extends MultiBuildItem {

private final Path artifactPath;
private final ArtifactKey artifact;
private final Set<String> resources;

public RemovedResourceBuildItem(ArtifactKey artifact, Set<String> resources) {
this.artifact = artifact;
this.resources = resources;
this.artifactPath = null;
}

/**
Expand All @@ -26,15 +29,30 @@ public RemovedResourceBuildItem(ArtifactKey artifact, Set<String> resources) {
*/
@Deprecated(forRemoval = true)
public RemovedResourceBuildItem(GACT artifact, Set<String> resources) {
this.artifactPath = null;
this.artifact = artifact;
this.resources = resources;
}

public RemovedResourceBuildItem(Path artifactPath, Set<String> resources) {
this.artifact = null;
this.resources = resources;
this.artifactPath = artifactPath;
}

public ArtifactKey getArtifact() {
return artifact;
}

public Set<String> getResources() {
return resources;
}

public Path getArtifactPath() {
return this.artifactPath;
}

public boolean hasArtifact() {
return this.artifact != null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package io.quarkus.deployment.dev;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevModeCleanupBuildItem;
import io.quarkus.deployment.builditem.RemovedResourceBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.maven.dependency.ResolvedDependency;

/**
* Cleans up classes that is not needed in Prod
*/
public class DevModeCleanupProcessor {

/**
* This makes sure the Dev Mode Classes are not included in the prod build
*/
@BuildStep(onlyIf = IsNormal.class)
void cleanProd(BuildProducer<RemovedResourceBuildItem> producer,
List<DevModeCleanupBuildItem> devUIItemsToRemove,
CurateOutcomeBuildItem curateOutcomeBuildItem) {

Set<String> classNames = new HashSet<>();
Set<String> packageNames = new HashSet<>();
for (DevModeCleanupBuildItem devUIRemoveItemBuildItem : devUIItemsToRemove) {
if (devUIRemoveItemBuildItem.isWholePackage()) {
packageNames.addAll(devUIRemoveItemBuildItem.getPackageNames());
} else {
for (Class c : devUIRemoveItemBuildItem.getClasses()) {
classNames.add(c.getName());
}
}
}

List<RemovedResourceBuildItem> removedResourceBuildItems = getRemoveResourceBuildItemList(classNames, packageNames,
curateOutcomeBuildItem);
producer.produce(removedResourceBuildItems);
}

private List<RemovedResourceBuildItem> getRemoveResourceBuildItemList(Set<String> classNames, Set<String> packageNames,
CurateOutcomeBuildItem curateOutcomeBuildItem) {
List<RemovedResourceBuildItem> buildItems = new ArrayList<>();

ApplicationModel applicationModel = curateOutcomeBuildItem.getApplicationModel();
Collection<ResolvedDependency> runtimeDependencies = applicationModel.getRuntimeDependencies();
List<ResolvedDependency> allArtifacts = new ArrayList<>(runtimeDependencies.size() + 1);
allArtifacts.addAll(runtimeDependencies);
allArtifacts.add(applicationModel.getAppArtifact());
for (ResolvedDependency i : allArtifacts) {
for (Path path : i.getResolvedPaths()) {
if (path.toString().endsWith(".jar") && path.toFile().isFile()) {
Set<String> foundClasses = new HashSet<>();
try (JarFile jarFile = new JarFile(path.toFile())) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
String entryClassName = entry.getName()
.replace('/', '.')
.replace(".class", "");
if (classNames.contains(entryClassName)) {
foundClasses.add(entry.getName());
} else if (packageNames.stream().anyMatch(entryClassName::startsWith)) {
foundClasses.add(entry.getName());
}
}
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
if (!foundClasses.isEmpty()) {
buildItems.add(new RemovedResourceBuildItem(path, foundClasses));
}
}
}
}

return buildItems;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,14 @@ private void handleRemovedResources(ClassLoadingConfig classLoadingConfig, Curat
removed.put(new GACT(entry.getKey().split(":")), entry.getValue());
}
for (RemovedResourceBuildItem i : removedResourceBuildItems) {
removed.computeIfAbsent(i.getArtifact(), k -> new HashSet<>()).addAll(i.getResources());
if (i.hasArtifact()) {
removed.computeIfAbsent(i.getArtifact(), k -> new HashSet<>()).addAll(i.getResources());
} else {
transformedClassesByJar.computeIfAbsent(i.getArtifactPath(), s -> new HashSet<>())
.addAll(i.getResources().stream()
.map(file -> new TransformedClassesBuildItem.TransformedClass(null, null, file))
.collect(Collectors.toSet()));
}
}
if (!removed.isEmpty()) {
ApplicationModel applicationModel = curateOutcomeBuildItem.getApplicationModel();
Expand All @@ -332,7 +339,7 @@ private void handleRemovedResources(ClassLoadingConfig classLoadingConfig, Curat
for (Path path : i.getResolvedPaths()) {
transformedClassesByJar.computeIfAbsent(path, s -> new HashSet<>())
.addAll(filtered.stream()
.map(file -> new TransformedClassesBuildItem.TransformedClass(null, null, file, false))
.map(file -> new TransformedClassesBuildItem.TransformedClass(null, null, file))
.collect(Collectors.toSet()));
}
}
Expand Down
13 changes: 13 additions & 0 deletions docs/src/main/asciidoc/dev-ui.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,19 @@ JsonRPCProvidersBuildItem createJsonRPCServiceForCache() {// <2>

https://github.com/quarkusio/quarkus/blob/main/extensions/cache/deployment/src/main/java/io/quarkus/cache/deployment/devui/CacheDevUiProcessor.java[Example code]

Also cleanup this in Prod mode, so when the app build in prod mode, we remove all dev-ui related runtime classes:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this pops up kinda in the middle in jsonrpc explanation. Maybe have a more explicit section/title like "Remove devUI from Production" ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only really apply to Dev UI that has jsonrpc services. Many extensions does not have any. Any screen (js) and any build time data or runtime against the deployment cp is already not included in the prod build, it's just when you do the jsonrpc service, hence its added here


[source,java]
----
@BuildStep(onlyIf = IsNormal.class)// <1>
DevModeCleanupBuildItem cleanProd() {// <2>
return new DevModeCleanupBuildItem(CacheJsonRPCService.class, true);// <3>
}
----
<1> Always only do this in Normal (Prod) Mode
<2> Produce or return a `DevModeCleanupBuildItem`
<3> Define the class or classes in your runtime module that will be removed. You can also clean the whole package of that class in case you used more than just the JSON-RPC Service
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

show how to clean the whole package as otherwise one is left guessing :)


Now, in your Runtime module, create the JsonRPC Service. This class will default to an application-scoped bean, except if you explicitly scope the bean. All public methods that return something will be made available to call from the Web component Javascript.

The return object in these methods can be:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import io.quarkus.arc.runtime.devmode.EventsMonitor;
import io.quarkus.arc.runtime.devui.ArcJsonRPCService;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevModeCleanupBuildItem;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.devui.spi.page.CardPageBuildItem;
import io.quarkus.devui.spi.page.Page;
Expand Down Expand Up @@ -102,6 +104,14 @@ public CardPageBuildItem pages(ArcBeanInfoBuildItem arcBeanInfoBuildItem, ArcCon
return pageBuildItem;
}

@BuildStep(onlyIf = IsNormal.class)
void cleanProd(BuildProducer<DevModeCleanupBuildItem> producer) {
producer.produce(new DevModeCleanupBuildItem(
"io.quarkus.arc.runtime.devconsole",
"io.quarkus.arc.runtime.devmode",
"io.quarkus.arc.runtime.devui"));
}

@BuildStep(onlyIf = IsDevelopment.class)
JsonRPCProvidersBuildItem createJsonRPCService() {
return new JsonRPCProvidersBuildItem(ArcJsonRPCService.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import io.quarkus.cache.runtime.devui.CacheJsonRPCService;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevModeCleanupBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.devui.spi.page.CardPageBuildItem;
Expand All @@ -25,4 +27,9 @@ CardPageBuildItem create(CurateOutcomeBuildItem bi) {
JsonRPCProvidersBuildItem createJsonRPCServiceForCache() {
return new JsonRPCProvidersBuildItem(CacheJsonRPCService.class);
}

@BuildStep(onlyIf = IsNormal.class)
DevModeCleanupBuildItem cleanProd() {
return new DevModeCleanupBuildItem(CacheJsonRPCService.class, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import io.quarkus.container.image.runtime.devui.ContainerBuilderJsonRpcService;
import io.quarkus.container.spi.AvailableContainerImageExtensionBuildItem;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevModeCleanupBuildItem;
import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.dev.console.TempSystemProperties;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
Expand Down Expand Up @@ -43,6 +45,11 @@ JsonRPCProvidersBuildItem createJsonRPCServiceForContainerBuild() {
return new JsonRPCProvidersBuildItem(ContainerBuilderJsonRpcService.class);
}

@BuildStep(onlyIf = IsNormal.class)
DevModeCleanupBuildItem cleanProd() {
return new DevModeCleanupBuildItem(ContainerBuilderJsonRpcService.class, true);
}

private Function<Map<String, String>, String> build() {
return (map -> {
QuarkusBootstrap existing = (QuarkusBootstrap) DevConsoleManager.getQuarkusBootstrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig;
import io.quarkus.datasource.runtime.devui.DatasourceJsonRpcService;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevModeCleanupBuildItem;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.devui.spi.page.CardPageBuildItem;
import io.quarkus.devui.spi.page.Page;
Expand Down Expand Up @@ -35,4 +37,8 @@ JsonRPCProvidersBuildItem registerJsonRpcBackend() {
return new JsonRPCProvidersBuildItem(DatasourceJsonRpcService.class);
}

@BuildStep(onlyIf = IsNormal.class)
DevModeCleanupBuildItem cleanProd() {
return new DevModeCleanupBuildItem(DatasourceJsonRpcService.class, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

import io.quarkus.agroal.spi.JdbcInitialSQLGeneratorBuildItem;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.DevModeCleanupBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
import io.quarkus.devui.spi.page.CardPageBuildItem;
Expand Down Expand Up @@ -49,4 +51,9 @@ CardPageBuildItem create(FlywayDevUIRecorder recorder, FlywayBuildTimeConfig bui
JsonRPCProvidersBuildItem registerJsonRpcBackend() {
return new JsonRPCProvidersBuildItem(FlywayJsonRpcService.class);
}

@BuildStep(onlyIf = IsNormal.class)
DevModeCleanupBuildItem cleanProd() {
return new DevModeCleanupBuildItem(FlywayJsonRpcService.class, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.DevModeCleanupBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
Expand Down Expand Up @@ -231,6 +233,11 @@ JsonRPCProvidersBuildItem createJsonRPCServiceForCache() {
return new JsonRPCProvidersBuildItem(GrpcJsonRPCService.class);
}

@BuildStep(onlyIf = IsNormal.class)
void cleanProd(BuildProducer<DevModeCleanupBuildItem> producer) {
producer.produce(new DevModeCleanupBuildItem(GrpcJsonRPCService.class, true));
}

private Collection<Class<?>> getGrpcServices(IndexView index) throws ClassNotFoundException {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
Set<String> serviceClassNames = new HashSet<>();
Expand Down
Loading
Loading