Skip to content

Commit 7bf2de4

Browse files
committed
KOGITO-9415 Added Live Reload support for CodeGenProviders
Signed-off-by: Helber Belmiro <[email protected]>
1 parent 7320e6c commit 7bf2de4

File tree

14 files changed

+470
-10
lines changed

14 files changed

+470
-10
lines changed

kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void executeWorkItem(KogitoWorkItem workItem, KogitoWorkItemManager manag
4242

4343
protected abstract Object internalExecute(KogitoWorkItem workItem, Map<String, Object> parameters);
4444

45-
protected final <V> V buildBody(Map<String, Object> params, Class<V> clazz) {
45+
protected static <V> V buildBody(Map<String, Object> params, Class<V> clazz) {
4646
for (Object obj : params.values()) {
4747
if (obj != null && clazz.isAssignableFrom(obj.getClass())) {
4848
return clazz.cast(obj);

quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/KogitoQuarkusResourceUtils.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,15 @@ public static void registerResources(Collection<GeneratedFile> generatedFiles,
162162
}
163163

164164
public static IndexView generateAggregatedIndex(IndexView baseIndex, List<KogitoGeneratedClassesBuildItem> generatedKogitoClasses) {
165-
List<IndexView> indexes = new ArrayList<>();
166-
indexes.add(baseIndex);
167-
168-
indexes.addAll(generatedKogitoClasses.stream()
165+
return generateAggregatedIndexNew(baseIndex, generatedKogitoClasses.stream()
169166
.map(KogitoGeneratedClassesBuildItem::getIndexedClasses)
170167
.collect(Collectors.toList()));
168+
}
169+
170+
public static IndexView generateAggregatedIndexNew(IndexView baseIndex, List<IndexView> newIndexViews) {
171+
List<IndexView> indexes = new ArrayList<>();
172+
indexes.add(baseIndex);
173+
indexes.addAll(newIndexViews);
171174
return CompositeIndex.create(indexes.toArray(new IndexView[0]));
172175
}
173176

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.kie.kogito.quarkus.common.deployment;
17+
18+
import org.jboss.jandex.IndexView;
19+
20+
import io.quarkus.builder.item.SimpleBuildItem;
21+
22+
public final class LiveReloadExecutionBuildItem extends SimpleBuildItem {
23+
24+
private final IndexView indexView;
25+
26+
public LiveReloadExecutionBuildItem(IndexView indexView) {
27+
this.indexView = indexView;
28+
}
29+
30+
public IndexView getIndexView() {
31+
return indexView;
32+
}
33+
}

quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/ServerlessWorkflowAssetsProcessor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.kie.kogito.process.expr.ExpressionHandler;
3838
import org.kie.kogito.quarkus.common.deployment.KogitoAddonsPreGeneratedSourcesBuildItem;
3939
import org.kie.kogito.quarkus.common.deployment.KogitoBuildContextBuildItem;
40+
import org.kie.kogito.quarkus.common.deployment.LiveReloadExecutionBuildItem;
4041
import org.kie.kogito.quarkus.extensions.spi.deployment.KogitoProcessContainerGeneratorBuildItem;
4142
import org.kie.kogito.quarkus.serverless.workflow.WorkflowHandlerGeneratedFile;
4243
import org.kie.kogito.quarkus.serverless.workflow.WorkflowHandlerGenerator;
@@ -52,7 +53,6 @@
5253

5354
import io.quarkus.deployment.annotations.BuildProducer;
5455
import io.quarkus.deployment.annotations.BuildStep;
55-
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
5656
import io.quarkus.deployment.builditem.FeatureBuildItem;
5757
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
5858
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
@@ -84,9 +84,9 @@ NativeImageResourceBuildItem addExpressionHandlers(BuildProducer<ServiceProvider
8484
}
8585

8686
@BuildStep
87-
void addWorkItemHandlers(KogitoBuildContextBuildItem contextBI, CombinedIndexBuildItem indexBuildItem, BuildProducer<KogitoAddonsPreGeneratedSourcesBuildItem> sources) {
87+
void addWorkItemHandlers(KogitoBuildContextBuildItem contextBI, LiveReloadExecutionBuildItem liveReloadExecutionBuildItem, BuildProducer<KogitoAddonsPreGeneratedSourcesBuildItem> sources) {
8888
KogitoBuildContext context = contextBI.getKogitoBuildContext();
89-
IndexView index = indexBuildItem.getIndex();
89+
IndexView index = liveReloadExecutionBuildItem.getIndexView();
9090
Collection<GeneratedFile> generatedFiles = new ArrayList<>();
9191
for (WorkflowHandlerGenerator generator : generators) {
9292
for (WorkflowHandlerGeneratedFile generated : generator.generateHandlerClasses(context, index)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.kie.kogito.quarkus.serverless.workflow.deployment.livereload;
17+
18+
import java.util.Collection;
19+
20+
import org.drools.codegen.common.GeneratedFile;
21+
import org.jboss.jandex.IndexView;
22+
23+
final class CodeGenerationResult {
24+
25+
private final Collection<GeneratedFile> generatedFiles;
26+
27+
private final IndexView indexView;
28+
29+
CodeGenerationResult(Collection<GeneratedFile> generatedFiles, IndexView indexView) {
30+
this.generatedFiles = generatedFiles;
31+
this.indexView = indexView;
32+
}
33+
34+
Collection<GeneratedFile> getGeneratedFiles() {
35+
return generatedFiles;
36+
}
37+
38+
IndexView getIndexView() {
39+
return indexView;
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.kie.kogito.quarkus.serverless.workflow.deployment.livereload;
17+
18+
import java.io.IOException;
19+
import java.io.UncheckedIOException;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
22+
import java.util.ArrayList;
23+
import java.util.Collection;
24+
import java.util.HashSet;
25+
import java.util.List;
26+
import java.util.ServiceLoader;
27+
import java.util.stream.Stream;
28+
29+
import javax.inject.Inject;
30+
31+
import org.drools.codegen.common.GeneratedFile;
32+
import org.drools.codegen.common.GeneratedFileType;
33+
import org.drools.drl.quarkus.util.deployment.DroolsQuarkusResourceUtils;
34+
import org.eclipse.microprofile.config.ConfigProvider;
35+
import org.jboss.jandex.IndexView;
36+
import org.jboss.jandex.Indexer;
37+
import org.kie.kogito.codegen.api.context.KogitoBuildContext;
38+
import org.kie.kogito.quarkus.common.deployment.KogitoAddonsPreGeneratedSourcesBuildItem;
39+
import org.kie.kogito.quarkus.common.deployment.KogitoBuildContextBuildItem;
40+
import org.kie.kogito.quarkus.common.deployment.KogitoQuarkusResourceUtils;
41+
import org.kie.kogito.quarkus.common.deployment.LiveReloadExecutionBuildItem;
42+
43+
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
44+
import io.quarkus.bootstrap.model.ApplicationModel;
45+
import io.quarkus.bootstrap.prebuild.CodeGenException;
46+
import io.quarkus.deployment.CodeGenContext;
47+
import io.quarkus.deployment.IsDevelopment;
48+
import io.quarkus.deployment.annotations.BuildProducer;
49+
import io.quarkus.deployment.annotations.BuildStep;
50+
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
51+
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
52+
import io.quarkus.deployment.index.IndexingUtil;
53+
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
54+
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
55+
56+
/**
57+
* This class adds live reload support for {@link io.quarkus.deployment.CodeGenProvider} objects.
58+
*/
59+
public class LiveReloadProcessor {
60+
61+
private final LiveReloadBuildItem liveReloadBuildItem;
62+
63+
private final ApplicationModel applicationModel;
64+
65+
private final Path workDir;
66+
67+
private final IndexView computingIndex;
68+
69+
private final IndexView index;
70+
71+
private final KogitoBuildContext kogitoBuildContext;
72+
73+
@Inject
74+
public LiveReloadProcessor(
75+
CombinedIndexBuildItem combinedIndexBuildItem,
76+
LiveReloadBuildItem liveReloadBuildItem,
77+
CurateOutcomeBuildItem curateOutcomeBuildItem,
78+
OutputTargetBuildItem outputTargetBuildItem,
79+
KogitoBuildContextBuildItem contextBuildItem) {
80+
this.liveReloadBuildItem = liveReloadBuildItem;
81+
applicationModel = curateOutcomeBuildItem.getApplicationModel();
82+
workDir = outputTargetBuildItem.getOutputDirectory();
83+
computingIndex = combinedIndexBuildItem.getComputingIndex();
84+
index = combinedIndexBuildItem.getIndex();
85+
kogitoBuildContext = contextBuildItem.getKogitoBuildContext();
86+
}
87+
88+
@BuildStep(onlyIf = IsDevelopment.class)
89+
public LiveReloadExecutionBuildItem liveReload(BuildProducer<KogitoAddonsPreGeneratedSourcesBuildItem> sourcesProducer) {
90+
Collection<GeneratedFile> generatedFiles = new ArrayList<>();
91+
List<IndexView> indexViews = new ArrayList<>();
92+
if (liveReloadBuildItem.isLiveReload()) {
93+
if (shouldSkipLiveReload()) {
94+
dontSkipNextLiveReload();
95+
} else {
96+
ServiceLoader.load(LiveReloadableCodeGenProvider.class).stream()
97+
.map(ServiceLoader.Provider::get)
98+
.map(this::generateCode)
99+
.forEach(codeGenerationResult -> {
100+
generatedFiles.addAll(codeGenerationResult.getGeneratedFiles());
101+
indexViews.add(codeGenerationResult.getIndexView());
102+
});
103+
}
104+
}
105+
if (!generatedFiles.isEmpty()) {
106+
sourcesProducer.produce(new KogitoAddonsPreGeneratedSourcesBuildItem(generatedFiles));
107+
skipNextLiveReload();
108+
return new LiveReloadExecutionBuildItem(KogitoQuarkusResourceUtils.generateAggregatedIndexNew(computingIndex, indexViews));
109+
} else {
110+
dontSkipNextLiveReload();
111+
return new LiveReloadExecutionBuildItem(computingIndex);
112+
}
113+
}
114+
115+
private CodeGenerationResult generateCode(LiveReloadableCodeGenProvider codeGenProvider) {
116+
try {
117+
Collection<GeneratedFile> generatedFiles = new ArrayList<>(generateSources(codeGenProvider));
118+
if (!generatedFiles.isEmpty()) {
119+
Collection<GeneratedBeanBuildItem> generatedBeanBuildItems = compileGeneratedSources(generatedFiles);
120+
return new CodeGenerationResult(generatedFiles, indexCompiledSources(generatedBeanBuildItems));
121+
} else {
122+
return new CodeGenerationResult(List.of(), computingIndex);
123+
}
124+
} catch (CodeGenException e) {
125+
throw new IllegalStateException(e);
126+
} catch (IOException e) {
127+
throw new UncheckedIOException(e);
128+
}
129+
}
130+
131+
private IndexView indexCompiledSources(Collection<GeneratedBeanBuildItem> generatedBeanBuildItems) {
132+
Indexer kogitoIndexer = new Indexer();
133+
134+
for (GeneratedBeanBuildItem generatedBeanBuildItem : generatedBeanBuildItems) {
135+
IndexingUtil.indexClass(
136+
generatedBeanBuildItem.getName(),
137+
kogitoIndexer,
138+
index,
139+
new HashSet<>(),
140+
kogitoBuildContext.getClassLoader(),
141+
generatedBeanBuildItem.getData());
142+
}
143+
144+
return kogitoIndexer.complete();
145+
}
146+
147+
private Collection<GeneratedBeanBuildItem> compileGeneratedSources(Collection<GeneratedFile> sources) {
148+
return DroolsQuarkusResourceUtils.compileGeneratedSources(
149+
kogitoBuildContext,
150+
applicationModel.getRuntimeDependencies(),
151+
sources,
152+
true);
153+
}
154+
155+
private Collection<GeneratedFile> generateSources(LiveReloadableCodeGenProvider codeGenProvider)
156+
throws CodeGenException, IOException {
157+
Path outDir = workDir.resolve("generated-sources").resolve(codeGenProvider.providerId());
158+
var generatedFiles = new ArrayList<GeneratedFile>();
159+
for (Path sourcePath : kogitoBuildContext.getAppPaths().getSourcePaths()) {
160+
Path inputDir = sourcePath.resolve("main").resolve(codeGenProvider.inputDirectory());
161+
var codeGenContext = new CodeGenContext(applicationModel, outDir, workDir, inputDir, false, ConfigProvider.getConfig(), false);
162+
if (codeGenProvider.trigger(codeGenContext)) {
163+
try (Stream<Path> sources = Files.walk(outDir)) {
164+
sources.filter(Files::isRegularFile)
165+
.filter(path -> path.toString().endsWith(".java"))
166+
.map(path -> {
167+
try {
168+
return new GeneratedFile(GeneratedFileType.SOURCE, outDir.relativize(path), Files.readAllBytes(path));
169+
} catch (IOException e) {
170+
throw new UncheckedIOException(e);
171+
}
172+
})
173+
.forEach(generatedFiles::add);
174+
}
175+
}
176+
}
177+
178+
return generatedFiles;
179+
}
180+
181+
private void skipNextLiveReload() {
182+
liveReloadBuildItem.setContextObject(SkipLiveReload.class, SkipLiveReload.TRUE);
183+
}
184+
185+
private void dontSkipNextLiveReload() {
186+
liveReloadBuildItem.setContextObject(SkipLiveReload.class, SkipLiveReload.FALSE);
187+
}
188+
189+
private boolean shouldSkipLiveReload() {
190+
if (liveReloadBuildItem.getContextObject(SkipLiveReload.class) != null) {
191+
return liveReloadBuildItem.getContextObject(SkipLiveReload.class) == SkipLiveReload.TRUE;
192+
}
193+
return false;
194+
}
195+
196+
@BuildStep(onlyIfNot = IsDevelopment.class)
197+
public LiveReloadExecutionBuildItem executeWhenNotDevelopment() {
198+
return new LiveReloadExecutionBuildItem(computingIndex);
199+
}
200+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.kie.kogito.quarkus.serverless.workflow.deployment.livereload;
17+
18+
import io.quarkiverse.asyncapi.generator.input.AsyncApiGeneratorStreamCodeGen;
19+
20+
/**
21+
* Wrapper for {@link AsyncApiGeneratorStreamCodeGen} that implements the {@link LiveReloadableCodeGenProvider} Service Provider Interface.
22+
*/
23+
public class LiveReloadableAsyncApiGeneratorStreamCodeGen extends LiveReloadableCodeGenProviderBase<AsyncApiGeneratorStreamCodeGen> {
24+
25+
public LiveReloadableAsyncApiGeneratorStreamCodeGen() {
26+
super(new AsyncApiGeneratorStreamCodeGen());
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.kie.kogito.quarkus.serverless.workflow.deployment.livereload;
17+
18+
import io.quarkus.bootstrap.prebuild.CodeGenException;
19+
import io.quarkus.deployment.CodeGenContext;
20+
21+
/**
22+
* Service Provider Interface for {@link io.quarkus.deployment.CodeGenProvider} objects that need to be invoked on live reloads.
23+
*/
24+
interface LiveReloadableCodeGenProvider {
25+
26+
boolean trigger(CodeGenContext context) throws CodeGenException;
27+
28+
String providerId();
29+
30+
String inputDirectory();
31+
}

0 commit comments

Comments
 (0)