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

[KOGITO-9775] Classpath loading as fallback #3218

Merged
merged 2 commits into from
Sep 8, 2023
Merged
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
Expand Up @@ -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);
Expand All @@ -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<Path> internalGetPath() {
return Optional.empty();
}

@Override
public Optional<Path> getPath() {
return internalGetPath().or(() -> {
for (URIContentLoader contentLoader : fallbackContentLoaders) {
Optional<Path> alternativePath = contentLoader.getPath();
fjtirado marked this conversation as resolved.
Show resolved Hide resolved
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) {
fjtirado marked this conversation as resolved.
Show resolved Hide resolved
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<URL> resource;
private final String path;
private final String classpath;

ClassPathContentLoader(URI uri, Optional<ClassLoader> cl) {
super(uri);
this.path = getPath(uri);
this.resource = Optional.ofNullable(cl.orElse(Thread.currentThread().getContextClassLoader()).getResource(path));
ClassPathContentLoader(URI uri, Optional<ClassLoader> 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) {
Expand All @@ -49,13 +51,26 @@ public Optional<URL> getResource() {
return resource;
}

public String getPath() {
return path;
@Override
protected Optional<Path> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Path> internalGetPath() {
return Files.exists(path) ? Optional.of(path) : Optional.empty();
}

@Override
protected byte[] loadURI(URI uri) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import java.io.InputStream;
import java.net.URI;
import java.nio.file.Path;
import java.util.Optional;

public interface URIContentLoader {

Expand All @@ -25,4 +27,6 @@ public interface URIContentLoader {
InputStream getInputStream();

URIContentLoaderType type();

Optional<Path> getPath();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClassLoader> optionalCl = Optional.ofNullable(cl);
return finalURI == uri ? new ClassPathContentLoader(finalURI, optionalCl) : new ClassPathContentLoader(finalURI, optionalCl, new ClassPathContentLoader(uri, optionalCl));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -79,32 +75,18 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
}

public Optional<Path> 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> 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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"name": "publishEvent",
"type": "asyncapi",
"operation": "classpath:specs/asyncAPI.yaml#sendWait"
"operation": "specs/asyncAPI.yaml#sendWait"
}
],
"states": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"functions": [
{
"name": "echoFunction",
"operation": "classpath:specs/enum-parameter.yaml#echo"
"operation": "specs/enum-parameter.yaml#echo"
}
],
"states": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"name": "sayHello",
"type": "rpc",
"operation": "classpath:greeting.proto#Greeter#SayHello"
"operation": "greeting.proto#Greeter#SayHello"
}
],
"states": [
Expand Down