
org.apache.geronimo.microprofile.openapi.cdi.GeronimoOpenAPIExtension Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.geronimo.microprofile.openapi.cdi;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toSet;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.CDI;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.servlet.ServletContext;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import org.apache.geronimo.microprofile.openapi.config.GeronimoOpenAPIConfig;
import org.apache.geronimo.microprofile.openapi.impl.filter.FilterImpl;
import org.apache.geronimo.microprofile.openapi.impl.loader.DefaultLoader;
import org.apache.geronimo.microprofile.openapi.impl.loader.yaml.Yaml;
import org.apache.geronimo.microprofile.openapi.impl.model.PathsImpl;
import org.apache.geronimo.microprofile.openapi.impl.processor.AnnotatedMethodElement;
import org.apache.geronimo.microprofile.openapi.impl.processor.AnnotatedTypeElement;
import org.apache.geronimo.microprofile.openapi.impl.processor.AnnotationProcessor;
import org.apache.geronimo.microprofile.openapi.impl.processor.spi.NamingStrategy;
import org.apache.geronimo.microprofile.openapi.jaxrs.JacksonOpenAPIYamlBodyWriter;
import org.eclipse.microprofile.openapi.OASConfig;
import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.OASModelReader;
import org.eclipse.microprofile.openapi.models.OpenAPI;
public class GeronimoOpenAPIExtension implements Extension {
private final Collection> endpoints = new ArrayList<>();
private final Map openapis = new HashMap<>();
private GeronimoOpenAPIConfig config;
private AnnotationProcessor processor;
private boolean skipScan;
private Collection classes;
private Collection packages;
private Collection excludePackages;
private Collection excludeClasses;
private boolean jacksonIsPresent;
void init(@Observes final BeforeBeanDiscovery beforeBeanDiscovery) {
config = GeronimoOpenAPIConfig.create();
processor = new AnnotationProcessor(config, loadNamingStrategy(config), null);
skipScan = Boolean.parseBoolean(config.read(OASConfig.SCAN_DISABLE, "false"));
classes = getConfigCollection(OASConfig.SCAN_CLASSES);
packages = getConfigCollection(OASConfig.SCAN_PACKAGES);
excludePackages = getConfigCollection(OASConfig.SCAN_EXCLUDE_PACKAGES);
excludeClasses = getConfigCollection(OASConfig.SCAN_EXCLUDE_CLASSES);
try {
Yaml.getObjectMapper();
jacksonIsPresent = true;
} catch (final Error | RuntimeException e) {
// no-op
}
}
public MediaType getDefaultMediaType() {
return jacksonIsPresent ? new MediaType("text", "vnd.yaml") : APPLICATION_JSON_TYPE;
}
private NamingStrategy loadNamingStrategy(final GeronimoOpenAPIConfig config) {
return ofNullable(config.read("model.operation.naming.strategy", null))
.map(String::trim)
.filter(it -> !it.isEmpty())
.map(it -> {
try {
return Thread.currentThread().getContextClassLoader().loadClass(it).getConstructor().newInstance();
} catch (final InstantiationException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) {
throw new IllegalArgumentException(e);
} catch (final InvocationTargetException ite) {
throw new IllegalArgumentException(ite.getTargetException());
}
})
.map(NamingStrategy.class::cast)
.orElseGet(NamingStrategy.Default::new);
}
void vetoJacksonIfNotHere(@Observes final ProcessAnnotatedType event) {
if (!jacksonIsPresent) {
event.veto();
}
}
protected void findEndpointsAndApplication(@Observes final ProcessBean event) {
final String typeName = event.getAnnotated().getBaseType().getTypeName();
if (classes == null && !skipScan && event.getAnnotated().isAnnotationPresent(Path.class) &&
!typeName.startsWith("org.apache.geronimo.microprofile.openapi.") &&
(packages == null || packages.stream().anyMatch(typeName::startsWith))) {
endpoints.add(event.getBean());
}
}
public OpenAPI getOrCreateOpenAPI(final Application application) {
if (classes != null) {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
return openapis.computeIfAbsent(application,
app -> createOpenApi(application.getClass(), classes.stream().map(c -> {
try {
return loader.loadClass(c);
} catch (final ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
})));
}
if (packages == null && (!application.getSingletons().isEmpty() || !application.getClasses().isEmpty())) {
return openapis.computeIfAbsent(application,
app -> createOpenApi(application.getClass(), Stream.concat(endpoints.stream().map(Bean::getBeanClass),
Stream.concat(app.getClasses().stream(), app.getSingletons().stream().map(Object::getClass)))));
}
return openapis.computeIfAbsent(application,
app -> createOpenApi(application.getClass(), endpoints.stream().map(Bean::getBeanClass)));
}
private Collection getConfigCollection(final String key) {
return ofNullable(config.read(key, null))
.map(vals -> Stream.of(vals.split(",")).map(String::trim).filter(v -> !v.isEmpty()).collect(toSet()))
.orElse(null);
}
private OpenAPI createOpenApi(final Class> application, final Stream> beans) {
final CDI
© 2015 - 2025 Weber Informatics LLC | Privacy Policy