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

Add MP OpenAPI support to Helidon MP #712

Merged
merged 49 commits into from
May 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e029bbb
Preliminary OpenAPI support for SE
tjquinno Apr 4, 2019
e9ce496
Additional files for prelim OpenAPI support in SE
tjquinno Apr 4, 2019
2b52033
Move test to correct package
tjquinno Apr 4, 2019
2262d32
Style fixes, add module-info and package-info
tjquinno Apr 5, 2019
5e9906e
Add extension point to OpenAPISupport for dealing with annotated clas…
tjquinno Apr 7, 2019
8a10055
Add use of Jandex for OpenAPI anno handling - not used for SE but wil…
tjquinno Apr 10, 2019
23b6526
Merge remote-tracking branch 'origin/master' into add-openapi
tjquinno Apr 10, 2019
2f76582
Use correct Helidon version in the pom
tjquinno Apr 10, 2019
f3d4850
Fix copyright date in MediaType.java
tjquinno Apr 10, 2019
b1c9c5e
Fix checkstyle complaint
tjquinno Apr 10, 2019
3868880
Rework some of the code to avoid spotbugs errors about open streams e…
tjquinno Apr 10, 2019
d640e6f
Merge remote-tracking branch 'origin/master' into add-openapi
tjquinno Apr 18, 2019
875a67e
Add unit tests and some refinements to OpenAPISupport class
tjquinno Apr 19, 2019
27901d2
Merge remote-tracking branch 'origin/master' into add-openapi
tjquinno Apr 19, 2019
ce69095
Tried adding copyright to field in yaml; worked locally but not in pi…
tjquinno Apr 19, 2019
444c67f
Ah ha! Fixed copyright the right way
tjquinno Apr 19, 2019
f20fbd6
Add tests to check return media type; add generic yaml MT to MediaType
tjquinno Apr 20, 2019
cf07608
Add JavaDoc comment to new public static
tjquinno Apr 20, 2019
9dd51fb
Add more tests, refactor common code into public static methods, add …
tjquinno Apr 22, 2019
b4bc8f8
Refactor utility methods to a new class to clarify ServerTest
tjquinno Apr 23, 2019
a674a2b
Some minor method renaming, reformatting, etc.
tjquinno Apr 23, 2019
c6f709b
Minor JavaDoc edit
tjquinno Apr 23, 2019
4e7929c
Add test to make sure the filter removes the expected path from the O…
tjquinno Apr 23, 2019
46f1bba
Clean up some Optional usages
tjquinno Apr 23, 2019
5bbb80d
Merge remote-tracking branch 'origin/master' into add-openapi
tjquinno Apr 23, 2019
39cd982
Refine annotation handling suppression to simplify use from MP
tjquinno Apr 24, 2019
6909fc6
Refactoring of SE OpenAPI support to simplify MP case; initial work f…
tjquinno Apr 26, 2019
69873c8
Changes to get Helidon MP OpenAPI support to bootstrap during start-up
tjquinno Apr 30, 2019
41bcf3a
Minor change in how config is retrieved in OpenAPIMpService
tjquinno May 2, 2019
d30869c
Merge with concurrent changes on main master
tjquinno May 6, 2019
d7a42f6
resolve a few conflicts
tjquinno May 6, 2019
5d598ad
Simple test involving the MP server and OpenAPI
tjquinno May 8, 2019
3f5cea8
Add some JavaDoc to a test util method
tjquinno May 8, 2019
294d94a
Updates to MP OpenAPI support; mostly to use Jandex to create an in-m…
tjquinno May 17, 2019
467d352
Slight improvement to pom descriptions; some checkstyle, spotbugs fixes
tjquinno May 18, 2019
0a7b4df
Add MP OpenAPI TCK runner to list of modules in Helidon MP TCKs; impr…
tjquinno May 18, 2019
c4826a5
Declare MP OpenAPI version at top-level Helidon pom; remove prop. dec…
tjquinno May 19, 2019
224badb
Merge remote-tracking branch 'origin/master' into add-openapi-mp
tjquinno May 19, 2019
7ed286c
Fix versions after merge with concurrent changes in main repo
tjquinno May 19, 2019
f14769f
Refine changes to Arq container to support OpenAPI while not breaking…
tjquinno May 20, 2019
684e553
Add comment to pom explaining jandex usage for test
tjquinno May 20, 2019
38892cb
Fix checkstyle, copyright errors
tjquinno May 20, 2019
1c50b70
Fix javadoc errors
tjquinno May 20, 2019
76da32a
Deal with spotbugs errors (including a false positive)
tjquinno May 20, 2019
ce2ffbf
Changes from code review
tjquinno May 20, 2019
e046963
Further code review changes
tjquinno May 20, 2019
fa25253
checkstyle error
tjquinno May 20, 2019
bbffafb
Incorporate review comments -- indentation
tjquinno May 21, 2019
c2ab5a7
OpenAPI should not be in our MP 1.2 bundle
tjquinno May 21, 2019
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
34 changes: 34 additions & 0 deletions microprofile/openapi/etc/spotbugs/exclude.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

-->

<FindBugsFilter
xmlns="https://github.com/spotbugs/filter/3.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">

<!-- We need the following because of SpotBugs issue 493
https://github.com/spotbugs/spotbugs/issues/493 (false positives with
try-with-resource blocks).
-->
<Match>
<Class name="io.helidon.microprofile.openapi.IndexBuilder"/>
<Method name="checkForIndexFile" />
<Bug pattern="OBL_UNSATISFIED_OBLIGATION" />
</Match>
</FindBugsFilter>
107 changes: 107 additions & 0 deletions microprofile/openapi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-project</artifactId>
<version>1.1.1-SNAPSHOT</version>
</parent>
<groupId>io.helidon.microprofile.openapi</groupId>
<artifactId>helidon-microprofile-openapi</artifactId>

<name>Helidon Microprofile OpenAPI</name>

<description>
Helidon Microprofile OpenAPI implementation
</description>

<packaging>jar</packaging>

<properties>
<spotbugs.exclude>etc/spotbugs/exclude.xml</spotbugs.exclude>
</properties>

<build>
<plugins>
<plugin>
<!--
Used for the BasicServerTest.
-->
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<executions>
<execution>
<id>make-test-index</id>
<goals>
<goal>jandex</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<classesDir>${project.build.directory}/test-classes</classesDir>
<fileSets>
<fileSet>
<directory>${project.build.directory}/test-classes</directory>
</fileSet>
</fileSets>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.server</groupId>
<artifactId>helidon-microprofile-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.openapi</groupId>
<artifactId>helidon-openapi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.helidon.microprofile.openapi;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;

import org.jboss.jandex.Index;
import org.jboss.jandex.IndexReader;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;

/**
* Portable extension to allow construction of a Jandex index (to pass to
* SmallRye OpenAPI) from CDI if no {@code META-INF/jandex.idx} file exists on
* the class path.
*/
public class IndexBuilder implements Extension {

private static final String INDEX_PATH = "/META-INF/jandex.idx";

private static final Logger LOGGER = Logger.getLogger(IndexBuilder.class.getName());

private final boolean isIndexPresentOnClasspath;

private final Set<Class<?>> annotatedTypes = new HashSet<>();

/**
* Creates a new instance of the index builder.
*
* @throws IOException in case of error checking for the Jandex index file
*/
public IndexBuilder() throws IOException {
isIndexPresentOnClasspath = checkForIndexFile();
if (isIndexPresentOnClasspath) {
LOGGER.log(Level.FINE, () -> String.format("Index file %s was located and will be used", INDEX_PATH));
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
} else {
LOGGER.log(Level.INFO, () -> String.format(
"OpenAPI support could not locate the index file %s "
+ "so will build an in-memory index. This slows your app start-up.%n"
+ "Consider using the jandex maven plug-in to build the index "
+ "and add it to your app at build-time",
INDEX_PATH));
}
}

/**
* Records each type that is annotated unless a Jandex index was found on
* the classpath (in which case we do not need to build our own in memory).
*
* @param <X> annotated type
* @param event {@code ProcessAnnotatedType} event
*/
private <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> type) {
if (isIndexPresentOnClasspath) {
return;
}
Class<?> c = type.getAnnotatedType().getJavaClass();
annotatedTypes.add(c);
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Reports an {@link IndexView} for the Jandex index that describes
* annotated classes for endpoints.
*
* @return {@code IndexView} describing discovered classes
* @throws IOException in case of error reading an existing index file or
* reading class bytecode from the classpath
*/
public IndexView indexView() throws IOException {
return isIndexPresentOnClasspath ? existingIndexFileReader() : indexFromHarvestedClasses();
}

private IndexView existingIndexFileReader() throws IOException {
try (InputStream jandexIS = getClass().getResourceAsStream(INDEX_PATH)) {
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
if (jandexIS == null) {
throw new IllegalArgumentException("Attempted to read from previously-located index file "
+ INDEX_PATH + " but the file cannot be found");
}
LOGGER.log(Level.FINE, "Using Jandex index at {0}", INDEX_PATH);
return new IndexReader(jandexIS).read();
}
}

private IndexView indexFromHarvestedClasses() throws IOException {
Indexer indexer = new Indexer();
for (Class<?> c : annotatedTypes) {
try (InputStream is = contextClassLoader().getResourceAsStream(resourceNameForClass(c))) {
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
if (is == null) {
throw new IllegalArgumentException("Cannot load bytecode from class "
+ c.getName() + " at " + resourceNameForClass(c)
+ " for annotation processing");
}
indexer.index(is);
}
}

LOGGER.log(Level.FINE, "Using internal Jandex index created from CDI bean discovery");
Index result = indexer.complete();
dumpIndex(Level.FINER, result);
return result;
}

private boolean checkForIndexFile() throws IOException {
try (InputStream jandexIS = getClass().getResourceAsStream(INDEX_PATH)) {
return jandexIS != null;
}
}

private static void dumpIndex(Level level, Index index) throws UnsupportedEncodingException {
if (LOGGER.isLoggable(level)) {
LOGGER.log(level, "Dump of internal Jandex index:");
PrintStream oldStdout = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream newPS = new PrintStream(baos, true, Charset.defaultCharset().name())) {
System.setOut(newPS);
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
index.printAnnotations();
index.printSubclasses();
LOGGER.log(level, baos.toString(Charset.defaultCharset().name()));
} finally {
System.setOut(oldStdout);
}
}
}

private static ClassLoader contextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}

private static String resourceNameForClass(Class<?> c) {
return c.getName().replace('.', '/') + ".class";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.helidon.microprofile.openapi;

import java.util.Optional;

import io.helidon.openapi.OpenAPISupport;

import io.smallrye.openapi.api.OpenApiConfig;
import org.jboss.jandex.IndexView;

/**
* Fluent builder for OpenAPISupport in Helidon MP.
*/
public final class MPOpenAPIBuilder extends OpenAPISupport.Builder {

private Optional<OpenApiConfig> openAPIConfig;
private Optional<IndexView> indexView;

@Override
public OpenApiConfig openAPIConfig() {
return openAPIConfig.get();
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public IndexView indexView() {
return indexView.get();
}

/**
* Sets the OpenApiConfig instance to use in governing the behavior of the
* smallrye OpenApi implementation.
*
* @param config {@link OpenApiConfig} instance to control OpenAPI behavior
* @return updated builder instance
*/
public MPOpenAPIBuilder openAPIConfig(OpenApiConfig config) {
this.openAPIConfig = Optional.of(config);
return this;
}

/**
* Sets the IndexView instance to be passed to the smallrye OpenApi impl for
* annotation analysis.
*
* @param indexView {@link IndexView} instance containing endpoint classes
* @return updated builder instance
*/
public MPOpenAPIBuilder indexView(IndexView indexView) {
this.indexView = Optional.of(indexView);
return this;
}

@Override
public void validate() throws IllegalStateException {
if (!openAPIConfig.isPresent()) {
throw new IllegalStateException("OpenApiConfig has not been set in MPBuilder");
}
}

}
Loading