org.jzenith.rest.RestPlugin Maven / Gradle / Ivy
The newest version!
/**
* Copyright © 2018 Marcus Thiesen ([email protected])
*
* 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 org.jzenith.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.inject.Injector;
import com.google.inject.Module;
import io.micrometer.core.instrument.MeterRegistry;
import io.opentracing.Tracer;
import io.opentracing.contrib.jaxrs2.server.ServerTracingDynamicFeature;
import io.opentracing.rxjava2.TracingRxJava2Utils;
import io.opentracing.util.GlobalTracer;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerOptions;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.jboss.resteasy.plugins.guice.GuiceResourceFactory;
import org.jboss.resteasy.plugins.server.vertx.VertxRegistry;
import org.jboss.resteasy.plugins.server.vertx.VertxResourceFactory;
import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jzenith.core.AbstractPlugin;
import org.jzenith.core.util.CompletableFutureHandler;
import org.jzenith.rest.docs.CustomOpenApiResource;
import org.jzenith.rest.exception.ConstantMessageThrowableMapping;
import org.jzenith.rest.exception.ThrowableMapping;
import org.jzenith.rest.exception.ValidationThrowableMapping;
import org.jzenith.rest.health.HealthCheckResource;
import org.jzenith.rest.metrics.MetricsFeature;
import org.jzenith.rest.metrics.PrometheusResource;
import javax.validation.ValidationException;
import javax.ws.rs.container.DynamicFeature;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Slf4j
public class RestPlugin extends AbstractPlugin {
private static final ImmutableList MODULES = ImmutableList.of(new RestBinder());
private static final ImmutableList> DEFAULT_RESOURCES = ImmutableList.of(PrometheusResource.class, CustomOpenApiResource.class, HealthCheckResource.class);
private final List> resources;
private final Map, ThrowableMapping>> exceptionMappings = Maps.newHashMap();
public RestPlugin(Collection> resources) {
this.resources = ImmutableList.copyOf(Iterables.concat(resources, DEFAULT_RESOURCES));
exceptionMappings.put(Exception.class, new ConstantMessageThrowableMapping<>(Exception.class, 500, "Unknown error"));
exceptionMappings.put(ValidationException.class, new ValidationThrowableMapping());
}
public static RestPlugin withResources(Class>... resources) {
return new RestPlugin(Arrays.asList(resources).stream()
.collect(ImmutableList.toImmutableList()));
}
@Override
protected List getModules() {
return ImmutableList.copyOf(Iterables.concat(MODULES, ImmutableList.of(new ResourceModule(resources))));
}
@Override
protected CompletableFuture start(Injector injector) {
if (log.isDebugEnabled()) {
log.debug("jZenith Rest is starting and registering the following resources:\n{}", Joiner.on('\n').join(resources));
}
initRxJavaTracing();
final GuiceVertxRequestHandler handler = initResteasy(injector);
final RestConfiguration restConfiguration = injector.getInstance(RestConfiguration.class);
final Vertx vertx = injector.getInstance(Vertx.class);
System.setProperty("vertx.disableWebsockets", Boolean.TRUE.toString());
final CompletableFutureHandler completableFutureHandler = new CompletableFutureHandler<>();
final HttpServerOptions options = new HttpServerOptions()
.setTcpFastOpen(true)
.setTcpNoDelay(true)
.setTcpQuickAck(true);
vertx.deployVerticle(() -> new AbstractVerticle() {
@Override
public void start(Future startFuture) {
vertx.createHttpServer(options)
.requestHandler(handler)
.listen(restConfiguration.getPort(), restConfiguration.getHost(), ar -> {
if (ar.succeeded()) {
startFuture.complete(null);
} else {
startFuture.fail(ar.cause());
}
});
}
}, new DeploymentOptions().setInstances(Runtime.getRuntime().availableProcessors()),
completableFutureHandler.handler());
return completableFutureHandler.thenApply(aVoid -> {
log.debug("jZenith Rest Plugin started (listening to {}:{})",
restConfiguration.getHost(), restConfiguration.getPort());
return "Done";
});
}
private void initRxJavaTracing() {
if (GlobalTracer.isRegistered()) {
final Tracer tracer = GlobalTracer.get();
TracingRxJava2Utils.enableTracing(tracer);
}
}
private GuiceVertxRequestHandler initResteasy(@NonNull final Injector injector) {
final Vertx vertx = injector.getInstance(Vertx.class);
final VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.start();
final ResteasyProviderFactory providerFactory = deployment.getProviderFactory();
providerFactory.getServerDynamicFeatures().add(new MetricsFeature(injector.getInstance(MeterRegistry.class)));
if (GlobalTracer.isRegistered()) {
final DynamicFeature tracing = new ServerTracingDynamicFeature.Builder(GlobalTracer.get())
.build();
providerFactory.getServerDynamicFeatures().add(tracing);
}
exceptionMappings.forEach((clz, throwableMapping) -> providerFactory.getExceptionMappers().put(clz, throwableMapping.toExceptionHandler()));
final VertxRegistry registry = deployment.getRegistry();
resources.forEach(resourceClass ->
registry.addResourceFactory(new VertxResourceFactory(new GuiceResourceFactory(injector.getProvider(resourceClass), resourceClass))));
providerFactory.registerProviderInstance(new JacksonContextResolver(injector.getInstance(ObjectMapper.class)));
return new GuiceVertxRequestHandler(vertx, deployment);
}
public RestPlugin withMapping(@NonNull final Class extends Throwable> throwable, int statusCode) {
exceptionMappings.put(throwable, new ThrowableMapping<>(throwable, statusCode));
return this;
}
public RestPlugin withMapping(@NonNull final Class extends Throwable> throwable, int statusCode, @NonNull String message) {
exceptionMappings.put(throwable, new ConstantMessageThrowableMapping<>(throwable, statusCode, message));
return this;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy