diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/CachedContentLoader.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/CachedContentLoader.java index 77f94dbc3c7..c09df8a9b9c 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/CachedContentLoader.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/CachedContentLoader.java @@ -18,9 +18,16 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URI; +import java.nio.file.Path; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class CachedContentLoader implements URIContentLoader { + private static final Logger logger = LoggerFactory.getLogger(CachedContentLoader.class); + private static class NoCopyByteArrayInputStream extends ByteArrayInputStream { public NoCopyByteArrayInputStream(byte[] buf) { super(buf); @@ -39,14 +46,46 @@ public synchronized byte[] readAllBytes() { } private final URI uri; + private URIContentLoader[] fallbackContentLoaders; - protected CachedContentLoader(URI uri) { + protected CachedContentLoader(URI uri, URIContentLoader... fallbackContentLoaders) { this.uri = uri; + this.fallbackContentLoaders = fallbackContentLoaders; + } + + protected Optional internalGetPath() { + return Optional.empty(); + } + + @Override + public Optional getPath() { + return internalGetPath().or(() -> { + for (URIContentLoader contentLoader : fallbackContentLoaders) { + Optional alternativePath = contentLoader.getPath(); + if (alternativePath.isPresent()) { + return alternativePath; + } + } + return Optional.empty(); + }); } @Override public InputStream getInputStream() { - return new NoCopyByteArrayInputStream(ResourceCacheFactory.getCache().get(uri, this::loadURI)); + try { + return new NoCopyByteArrayInputStream(ResourceCacheFactory.getCache().get(uri, this::loadURI)); + } catch (RuntimeException ex) { + for (URIContentLoader contentLoader : fallbackContentLoaders) { + try { + InputStream stream = contentLoader.getInputStream(); + logger.warn("URI {} was retrieved using a fallback mechanism {} rather than original {}", uri, contentLoader.type(), type()); + return stream; + } catch (RuntimeException supressed) { + ex.addSuppressed(supressed); + } + } + throw ex; + } } protected abstract byte[] loadURI(URI uri); diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java index 81f0b4f6c4c..f8895a1e61e 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java @@ -19,18 +19,20 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Path; import java.util.Optional; public class ClassPathContentLoader extends CachedContentLoader { private final Optional resource; - private final String path; + private final String classpath; - ClassPathContentLoader(URI uri, Optional cl) { - super(uri); - this.path = getPath(uri); - this.resource = Optional.ofNullable(cl.orElse(Thread.currentThread().getContextClassLoader()).getResource(path)); + ClassPathContentLoader(URI uri, Optional cl, URIContentLoader... fallbackContentLoaders) { + super(uri, fallbackContentLoaders); + this.classpath = getPath(uri); + this.resource = Optional.ofNullable(cl.orElse(Thread.currentThread().getContextClassLoader()).getResource(classpath)); } static String getPath(URI uri) { @@ -49,13 +51,26 @@ public Optional getResource() { return resource; } - public String getPath() { - return path; + @Override + protected Optional internalGetPath() { + return resource.map(ClassPathContentLoader::fromURL); + } + + String classpath() { + return classpath; + } + + private static Path fromURL(URL url) { + try { + return Path.of(url.toURI()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid URI " + url, e); + } } @Override protected byte[] loadURI(URI uri) { - return resource.map(this::loadBytes).orElseThrow(() -> new IllegalArgumentException("cannot find classpath resource " + path)); + return resource.map(this::loadBytes).orElseThrow(() -> new IllegalArgumentException("cannot find classpath resource " + classpath)); } private byte[] loadBytes(URL r) { diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/FileContentLoader.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/FileContentLoader.java index 11b7caa6963..d828429b57b 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/FileContentLoader.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/FileContentLoader.java @@ -20,25 +20,27 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; public class FileContentLoader extends CachedContentLoader { private final Path path; - FileContentLoader(URI uri) { - super(uri); + FileContentLoader(URI uri, URIContentLoader... fallbackContentLoaders) { + super(uri, fallbackContentLoaders); this.path = Path.of(getPath(uri)); } - public Path getPath() { - return path; - } - @Override public URIContentLoaderType type() { return URIContentLoaderType.FILE; } + @Override + protected Optional internalGetPath() { + return Files.exists(path) ? Optional.of(path) : Optional.empty(); + } + @Override protected byte[] loadURI(URI uri) { try { diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoader.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoader.java index 531cfc74e97..00a4608090e 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoader.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoader.java @@ -17,6 +17,8 @@ import java.io.InputStream; import java.net.URI; +import java.nio.file.Path; +import java.util.Optional; public interface URIContentLoader { @@ -25,4 +27,6 @@ public interface URIContentLoader { InputStream getInputStream(); URIContentLoaderType type(); + + Optional getPath(); } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java index 02bf30edb09..c60e5dea0f6 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java @@ -123,17 +123,16 @@ public Builder withBaseURI(URI baseURI) { } public URIContentLoader build() { - if (baseURI != null) { - uri = compoundURI(baseURI, uri); - } - switch (URIContentLoaderType.from(uri)) { + final URI finalURI = baseURI != null ? compoundURI(baseURI, uri) : uri; + switch (URIContentLoaderType.from(finalURI)) { default: case FILE: - return new FileContentLoader(uri); + return new FileContentLoader(finalURI, new ClassPathContentLoader(uri, Optional.ofNullable(cl))); case HTTP: - return new HttpContentLoader(uri, Optional.ofNullable(workflow), authRef); + return new HttpContentLoader(finalURI, Optional.ofNullable(workflow), authRef); case CLASSPATH: - return new ClassPathContentLoader(uri, Optional.ofNullable(cl)); + Optional optionalCl = Optional.ofNullable(cl); + return finalURI == uri ? new ClassPathContentLoader(finalURI, optionalCl) : new ClassPathContentLoader(finalURI, optionalCl, new ClassPathContentLoader(uri, optionalCl)); } } } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoaderTest.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoaderTest.java index 0e58c01ed03..205a2ec6f36 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoaderTest.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoaderTest.java @@ -44,7 +44,7 @@ void testPrefixSlashPath() { void testPath(String prefix) { ClassPathContentLoader contentLoader = new ClassPathContentLoader(URI.create(prefix + PATH), Optional.empty()); - assertThat(contentLoader.getPath()).isEqualTo(PATH); + assertThat(contentLoader.classpath()).isEqualTo(PATH); } } diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/rpc/WorkflowRPCCodeGenProvider.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/rpc/WorkflowRPCCodeGenProvider.java index dfe9e4898dc..4ad695f4d03 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/rpc/WorkflowRPCCodeGenProvider.java +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/rpc/WorkflowRPCCodeGenProvider.java @@ -16,8 +16,6 @@ package org.kie.kogito.quarkus.serverless.workflow.rpc; import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; @@ -27,8 +25,6 @@ import org.kie.kogito.quarkus.serverless.workflow.WorkflowCodeGenUtils; import org.kie.kogito.quarkus.serverless.workflow.WorkflowOperationResource; -import org.kie.kogito.serverless.workflow.io.ClassPathContentLoader; -import org.kie.kogito.serverless.workflow.io.FileContentLoader; import org.kie.kogito.serverless.workflow.io.URIContentLoader; import org.kie.kogito.serverless.workflow.io.URIContentLoaderFactory; import org.slf4j.Logger; @@ -79,32 +75,18 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { } public Optional getPath(WorkflowOperationResource resource, Path outputPath) { + logger.debug("Checking if resource {} should be written to {}", resource, outputPath); URIContentLoader contentLoader = resource.getContentLoader(); - logger.debug("Checking if resource {} should be writen to {}", resource, outputPath); - switch (contentLoader.type()) { - case FILE: - return Optional.of(((FileContentLoader) contentLoader).getPath()); - case CLASSPATH: - return ((ClassPathContentLoader) contentLoader).getResource().map(this::fromURL); - case HTTP: - try { - Path tempPath = outputPath.resolve(resource.getOperationId().getFileName()); - Files.write(tempPath, URIContentLoaderFactory.readAllBytes(contentLoader)); - return Optional.of(tempPath); - } catch (IOException io) { - throw new IllegalStateException(io); - } - default: - logger.warn("Unsupported content loader {}", contentLoader); - return Optional.empty(); + Optional path = contentLoader.getPath(); + if (path.isPresent()) { + return path; } - } - - private Path fromURL(URL url) { try { - return Path.of(url.toURI()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Invalid URI " + url, e); + Path tempPath = outputPath.resolve(resource.getOperationId().getFileName()); + Files.write(tempPath, URIContentLoaderFactory.readAllBytes(contentLoader)); + return Optional.of(tempPath); + } catch (IOException io) { + throw new IllegalStateException(io); } } diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/asyncPublisher.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/asyncPublisher.sw.json index df9faed48bf..92643e41450 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/asyncPublisher.sw.json +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/asyncPublisher.sw.json @@ -8,7 +8,7 @@ { "name": "publishEvent", "type": "asyncapi", - "operation": "classpath:specs/asyncAPI.yaml#sendWait" + "operation": "specs/asyncAPI.yaml#sendWait" } ], "states": [ diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/openAPIEnumParameter.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/openAPIEnumParameter.sw.json index 321be19f1a2..589cab4e150 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/openAPIEnumParameter.sw.json +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/openAPIEnumParameter.sw.json @@ -6,7 +6,7 @@ "functions": [ { "name": "echoFunction", - "operation": "classpath:specs/enum-parameter.yaml#echo" + "operation": "specs/enum-parameter.yaml#echo" } ], "states": [ diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json index 6eb109f7d2c..502ff1b6fa9 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json @@ -8,7 +8,7 @@ { "name": "sayHello", "type": "rpc", - "operation": "classpath:greeting.proto#Greeter#SayHello" + "operation": "greeting.proto#Greeter#SayHello" } ], "states": [