Skip to content

Commit

Permalink
Merge pull request #1475 from MikeEdgar/issue-1255
Browse files Browse the repository at this point in the history
Support for readers and filters to access the Jandex index
  • Loading branch information
phillip-kruger authored Jul 27, 2023
2 parents faa7cc2 + 4c3b3c3 commit c07993b
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 32 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ jobs:
java-version: 11

- name: maven cache
uses: actions/cache@v3
uses: actions/cache/restore@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2

- name: generate javadocs
run: mvn -B package javadoc:javadoc -DskipTests
run: mvn -B install javadoc:javadoc -DskipTests

tck-reporting:
runs-on: ubuntu-latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.OASModelReader;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;

import io.smallrye.openapi.api.OpenApiConfig;
import io.smallrye.openapi.api.OpenApiDocument;
Expand All @@ -27,6 +30,8 @@
*/
public class OpenApiProcessor {

static final IndexView EMPTY_INDEX = new Indexer().complete();

private OpenApiProcessor() {
}

Expand Down Expand Up @@ -71,8 +76,8 @@ public static OpenAPI bootstrap(OpenApiConfig config, IndexView index, ClassLoad
}
// Filter and model
if (config != null && classLoader != null) {
OpenApiDocument.INSTANCE.modelFromReader(modelFromReader(config, classLoader));
OpenApiDocument.INSTANCE.filter(getFilter(config, classLoader));
OpenApiDocument.INSTANCE.modelFromReader(modelFromReader(config, classLoader, index));
OpenApiDocument.INSTANCE.filter(getFilter(config, classLoader, index));
}

OpenApiDocument.INSTANCE.initialize();
Expand Down Expand Up @@ -169,20 +174,27 @@ public static OpenAPI modelFromAnnotations(OpenApiConfig config, ClassLoader loa
* @param config OpenApiConfig
* @param loader ClassLoader
* @return OpenApiImpl created from OASModelReader
*
* @deprecated use {@linkplain #modelFromReader(OpenApiConfig, ClassLoader, IndexView)} instead
*/
@Deprecated
public static OpenAPI modelFromReader(OpenApiConfig config, ClassLoader loader) {
String readerClassName = config.modelReader();
if (readerClassName == null) {
return null;
}
try {
Class<?> c = loader.loadClass(readerClassName);
OASModelReader reader = (OASModelReader) c.getDeclaredConstructor().newInstance();
return reader.buildModel();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
throw new OpenApiRuntimeException(e);
}
return modelFromReader(config, loader, EMPTY_INDEX);
}

/**
* Instantiate the configured {@link OASModelReader} and invoke it. If no reader is configured,
* then return null. If a class is configured but there is an error either instantiating or invoking
* it, a {@link OpenApiRuntimeException} is thrown.
*
* @param config OpenApiConfig
* @param loader ClassLoader
* @param index an IndexView to be provided to the filter when accepted via its constructor
* @return OpenApiImpl created from OASModelReader
*/
public static OpenAPI modelFromReader(OpenApiConfig config, ClassLoader loader, IndexView index) {
OASModelReader reader = newInstance(config.modelReader(), loader, index);
return reader != null ? reader.buildModel() : null;
}

/**
Expand All @@ -191,17 +203,49 @@ public static OpenAPI modelFromReader(OpenApiConfig config, ClassLoader loader)
* @param config OpenApiConfig
* @param loader ClassLoader
* @return OASFilter instance retrieved from loader
*
* @deprecated use {@linkplain #getFilter(OpenApiConfig, ClassLoader, IndexView)} instead
*/
@Deprecated
public static OASFilter getFilter(OpenApiConfig config, ClassLoader loader) {
String filterClassName = config.filter();
if (filterClassName == null) {
return getFilter(config, loader, EMPTY_INDEX);
}

/**
* Instantiate the {@link OASFilter} configured by the application.
*
* @param config OpenApiConfig
* @param loader ClassLoader
* @param index an IndexView to be provided to the filter when accepted via its constructor
* @return OASFilter instance retrieved from loader
*/
public static OASFilter getFilter(OpenApiConfig config, ClassLoader loader, IndexView index) {
return newInstance(config.filter(), loader, index);
}

@SuppressWarnings("unchecked")
static <T> T newInstance(String className, ClassLoader loader, IndexView index) {
if (className == null) {
return null;
}

Class<T> klazz = uncheckedCall(() -> (Class<T>) loader.loadClass(className));

return Arrays.stream(klazz.getDeclaredConstructors())
.filter(OpenApiProcessor::acceptsIndexView)
.findFirst()
.map(ctor -> uncheckedCall(() -> (T) ctor.newInstance(index)))
.orElseGet(() -> uncheckedCall(() -> klazz.getDeclaredConstructor().newInstance()));
}

private static boolean acceptsIndexView(Constructor<?> ctor) {
return ctor.getParameterCount() == 1 && IndexView.class.isAssignableFrom(ctor.getParameterTypes()[0]);
}

private static <T> T uncheckedCall(Callable<T> callable) {
try {
Class<?> c = loader.loadClass(filterClassName);
return (OASFilter) c.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
return callable.call();
} catch (Exception e) {
throw new OpenApiRuntimeException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.smallrye.openapi.runtime;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.UUID;

import org.jboss.jandex.IndexView;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class OpenApiProcessorTest {

ClassLoader loader;

@BeforeEach
void setup() {
this.loader = Thread.currentThread().getContextClassLoader();
}

@Test
void testNewInstanceWithNullClassName() {
Object instance = OpenApiProcessor.newInstance(null, loader, OpenApiProcessor.EMPTY_INDEX);
assertNull(instance);
}

@Test
void testNewInstanceWithNotFoundClassName() {
String invalidClassName = UUID.randomUUID().toString();

Throwable thrown = assertThrows(OpenApiRuntimeException.class,
() -> OpenApiProcessor.newInstance(invalidClassName, loader, OpenApiProcessor.EMPTY_INDEX));
assertEquals(ClassNotFoundException.class, thrown.getCause().getClass());
}

@Test
void testNewInstanceWithEmptyIndex() {
IndexAwareObject instance = OpenApiProcessor.newInstance(IndexAwareObject.class.getName(), loader,
OpenApiProcessor.EMPTY_INDEX);
assertNotNull(instance);
assertFalse(instance.defaultConstructorUsed);
assertEquals(0, instance.index.getKnownClasses().size());
}

@Test
void testNewInstanceWithIndexUnsupported() {
IndexUnawareObject instance = OpenApiProcessor.newInstance(IndexUnawareObject.class.getName(), loader,
OpenApiProcessor.EMPTY_INDEX);
assertNotNull(instance);
assertTrue(instance.defaultConstructorUsed);
}

static class IndexAwareObject {
IndexView index;
boolean defaultConstructorUsed;

IndexAwareObject() {
defaultConstructorUsed = true;
}

IndexAwareObject(IndexView index) {
this.index = index;
defaultConstructorUsed = false;
}

IndexAwareObject(Object other) {
defaultConstructorUsed = false;
}
}

static class IndexUnawareObject {
boolean defaultConstructorUsed;

IndexUnawareObject() {
defaultConstructorUsed = true;
}

IndexUnawareObject(Object other) {
defaultConstructorUsed = false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.util.Collection;
import java.util.Optional;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription;
import org.jboss.arquillian.container.spi.event.container.BeforeDeploy;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.jandex.IndexWriter;
import org.jboss.jandex.Indexer;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePaths;
Expand Down Expand Up @@ -114,7 +117,7 @@ private static void generateOpenAPI(final WebArchive war) {
ClassLoader contextClassLoader = currentThread().getContextClassLoader();

Optional<OpenAPI> annotationModel = ofNullable(modelFromAnnotations(openApiConfig, contextClassLoader, index));
Optional<OpenAPI> readerModel = ofNullable(modelFromReader(openApiConfig, contextClassLoader));
Optional<OpenAPI> readerModel = ofNullable(modelFromReader(openApiConfig, contextClassLoader, index));
Optional<OpenAPI> staticFileModel = Stream.of(modelFromFile(openApiConfig, war, "/META-INF/openapi.json", JSON),
modelFromFile(openApiConfig, war, "/META-INF/openapi.yaml", YAML),
modelFromFile(openApiConfig, war, "/META-INF/openapi.yml", YAML))
Expand All @@ -128,7 +131,7 @@ private static void generateOpenAPI(final WebArchive war) {
annotationModel.ifPresent(document::modelFromAnnotations);
readerModel.ifPresent(document::modelFromReader);
staticFileModel.ifPresent(document::modelFromStaticFile);
document.filter(getFilter(openApiConfig, contextClassLoader));
document.filter(getFilter(openApiConfig, contextClassLoader, index));
document.initialize();
OpenAPI openAPI = document.get();

Expand All @@ -139,6 +142,13 @@ private static void generateOpenAPI(final WebArchive war) {
// Ignore
}

try (ByteArrayOutputStream indexOut = new ByteArrayOutputStream()) {
new IndexWriter(indexOut).write(index);
war.addAsManifestResource(new ByteArrayAsset(indexOut.toByteArray()), "jandex.idx");
} catch (IOException e) {
throw new UncheckedIOException(e);
}

document.reset();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static io.smallrye.openapi.runtime.io.Format.JSON;
import static io.smallrye.openapi.runtime.io.Format.YAML;

import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Optional;
Expand All @@ -17,6 +18,8 @@
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.jboss.jandex.IndexReader;
import org.jboss.jandex.IndexView;
import org.jboss.resteasy.spi.ResteasyDeployment;

import io.smallrye.openapi.api.OpenApiConfig;
Expand Down Expand Up @@ -72,15 +75,21 @@ private Optional<OpenAPI> readOpenApiFile(final ServletContext servletContext, f
return Optional.empty();
}

final IndexView index;

try (InputStream indexStream = servletContext.getResourceAsStream("/META-INF/jandex.idx")) {
index = new IndexReader(indexStream).read();
}

final OpenApiDocument document = OpenApiDocument.INSTANCE;

try (OpenApiStaticFile staticFile = new OpenApiStaticFile(resource.openStream(), format)) {
Config config = ConfigProvider.getConfig();
OpenApiConfig openApiConfig = OpenApiConfig.fromConfig(config);
document.reset();
document.config(openApiConfig);
document.filter(OpenApiProcessor.getFilter(openApiConfig, Thread.currentThread().getContextClassLoader()));
document.modelFromStaticFile(
io.smallrye.openapi.runtime.OpenApiProcessor.modelFromStaticFile(openApiConfig, staticFile));
document.filter(OpenApiProcessor.getFilter(openApiConfig, Thread.currentThread().getContextClassLoader(), index));
document.modelFromStaticFile(OpenApiProcessor.modelFromStaticFile(openApiConfig, staticFile));
document.initialize();
return Optional.of(document.get());
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private OpenApiDocument generateSchema(

OpenAPI staticModel = generateStaticModel(openApiConfig, resourcesSrcDirs);
OpenAPI annotationModel = generateAnnotationModel(index, openApiConfig, SmallryeOpenApiTask.class.getClassLoader());
OpenAPI readerModel = OpenApiProcessor.modelFromReader(openApiConfig, classLoader);
OpenAPI readerModel = OpenApiProcessor.modelFromReader(openApiConfig, classLoader, index);

OpenApiDocument document = OpenApiDocument.INSTANCE;

Expand All @@ -146,7 +146,7 @@ private OpenApiDocument generateSchema(
addingModelDebug("static", staticModel);
document.modelFromStaticFile(staticModel);
}
document.filter(OpenApiProcessor.getFilter(openApiConfig, classLoader));
document.filter(OpenApiProcessor.getFilter(openApiConfig, classLoader, index));
document.initialize();

return document;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ private OpenApiDocument generateSchema(IndexView index) throws IOException, Depe

OpenAPI staticModel = generateStaticModel(openApiConfig);
OpenAPI annotationModel = generateAnnotationModel(index, openApiConfig, classLoader);
OpenAPI readerModel = OpenApiProcessor.modelFromReader(openApiConfig, classLoader);
OpenAPI readerModel = OpenApiProcessor.modelFromReader(openApiConfig, classLoader, index);

OpenApiDocument document = OpenApiDocument.newInstance();

Expand All @@ -304,7 +304,7 @@ private OpenApiDocument generateSchema(IndexView index) throws IOException, Depe
if (staticModel != null) {
document.modelFromStaticFile(staticModel);
}
document.filter(OpenApiProcessor.getFilter(openApiConfig, classLoader));
document.filter(OpenApiProcessor.getFilter(openApiConfig, classLoader, index));
document.initialize();

return document;
Expand Down

0 comments on commit c07993b

Please sign in to comment.