package org.jboss.resteasy.core;
import org.jboss.resteasy.resteasy_jaxrs.i18n.Messages;
import org.jboss.resteasy.spi.ApplicationException;
import org.jboss.resteasy.spi.ConstructorInjector;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.InternalServerErrorException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.ValueInjector;
import org.jboss.resteasy.spi.metadata.ConstructorParameter;
import org.jboss.resteasy.spi.metadata.ResourceConstructor;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.WebApplicationException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class ConstructorInjectorImpl implements ConstructorInjector
{
@SuppressWarnings("rawtypes")
protected Constructor constructor;
protected ValueInjector[] params;
public ConstructorInjectorImpl(final ResourceConstructor constructor, final ResteasyProviderFactory factory)
{
this.constructor = constructor.getConstructor();
params = new ValueInjector[constructor.getParams().length];
int i = 0;
for (ConstructorParameter parameter : constructor.getParams())
{
params[i++] = factory.getInjectorFactory().createParameterExtractor(parameter, factory);
}
}
public ConstructorInjectorImpl(@SuppressWarnings("rawtypes") final Constructor constructor, final ResteasyProviderFactory factory)
{
this.constructor = constructor;
params = new ValueInjector[constructor.getParameterTypes().length];
Parameter[] reflectionParameters = constructor.getParameters();
for (int i = 0; i < constructor.getParameterTypes().length; i++)
{
Class<?> type = constructor.getParameterTypes()[i];
Type genericType = constructor.getGenericParameterTypes()[i];
Annotation[] annotations = constructor.getParameterAnnotations()[i];
String name = reflectionParameters[i].getName();
params[i] = factory.getInjectorFactory().createParameterExtractor(constructor.getDeclaringClass(), constructor, name, type, genericType, annotations, factory);
}
}
@SuppressWarnings("unchecked")
@Override
public Object injectableArguments(HttpRequest input,
HttpResponse response,
boolean unwrapAsync)
{
if (params != null && params.length > 0)
{
Object[] args = new Object[params.length];
int i = 0;
CompletionStage<Void> stage = null;
for (ValueInjector extractor : params)
{
int ifinal = i++;
Object injectedObject = extractor.inject(input, response, unwrapAsync);
if (injectedObject != null && injectedObject instanceof CompletionStage) {
if (stage == null) stage = CompletableFuture.completedFuture(null);
stage = stage.thenCompose(v ->
((CompletionStage<Object>)injectedObject)
.thenAccept(value -> args[ifinal] = CompletionStageHolder.resolve(value)));
} else {
args[ifinal] = CompletionStageHolder.resolve(injectedObject);
}
}
if (stage == null) return args;
else return stage.thenApply(v -> args);
}
else
return null;
}
@SuppressWarnings("unchecked")
@Override
public Object injectableArguments(boolean unwrapAsync)
{
if (params != null && params.length > 0)
{
Object[] args = new Object[params.length];
int i = 0;
CompletionStage<Void> stage = null;
for (ValueInjector extractor : params)
{
int ifinal = i++;
Object injectedObject = extractor.inject(unwrapAsync);
if (injectedObject != null && injectedObject instanceof CompletionStage) {
if (stage == null) stage = CompletableFuture.completedFuture(null);
stage = stage.thenCompose(v -> ((CompletionStage<Object>)injectedObject).thenAccept(value -> args[ifinal] = CompletionStageHolder.resolve(value)));
} else {
args[ifinal] = CompletionStageHolder.resolve(injectedObject);
}
}
if (stage == null) return args;
else return stage.thenApply(v -> args);
}
else
return null;
}
public Object construct(HttpRequest request, HttpResponse httpResponse, boolean unwrapAsync) throws Failure, ApplicationException, WebApplicationException
{
Object obj = injectableArguments(request, httpResponse, unwrapAsync);
if (obj == null || !(obj instanceof CompletionStage)) {
return constructInRequest((Object[])obj);
}
@SuppressWarnings("unchecked")
CompletionStage<Object[]> stagedArgs = (CompletionStage<Object[]>)obj;
return stagedArgs.exceptionally(e -> {
Throwable t = e.getCause();
if (t != null) {
if (t instanceof NotFoundException) {
throw new NotFoundException(t.getMessage(), t.getCause());
} else if (t instanceof BadRequestException) {
throw new BadRequestException(t.getMessage(), t.getCause());
}
}
throw new InternalServerErrorException(
Messages.MESSAGES.failedProcessingArguments(constructor.toString()), e);
}).thenApply(args -> {
return constructInRequest(args);
});
}
protected Object constructInRequest(Object[] args) {
try
{
return constructor.newInstance(args);
}
catch (InstantiationException e)
{
throw new InternalServerErrorException(Messages.MESSAGES.failedToConstruct(constructor.toString()), e);
}
catch (IllegalAccessException e)
{
throw new InternalServerErrorException(Messages.MESSAGES.failedToConstruct(constructor.toString()), e);
}
catch (InvocationTargetException e)
{
Throwable cause = e.getCause();
if (cause instanceof WebApplicationException)
{
throw (WebApplicationException) cause;
}
throw new ApplicationException(Messages.MESSAGES.failedToConstruct(
constructor.toString()), e.getCause());
}
catch (IllegalArgumentException e)
{
String msg = Messages.MESSAGES.badArguments(constructor.toString() + " (");
boolean first = false;
for (Object arg : args)
{
if (!first)
{
first = true;
}
else
{
msg += ",";
}
if (arg == null)
{
msg += " null";
continue;
}
msg += " " + arg;
}
throw new InternalServerErrorException(msg, e);
}
}
@Override
public Object construct(boolean unwrapAsync)
{
Object obj = injectableArguments(unwrapAsync);
if (obj == null || !(obj instanceof CompletionStage)) {
return constructOutsideRequest((Object[])obj);
}
@SuppressWarnings("unchecked")
CompletionStage<Object[]> stagedArgs = (CompletionStage<Object[]>)obj;
return stagedArgs
.thenApply(args -> {
return constructOutsideRequest(args);
});
}
protected Object constructOutsideRequest(Object[] args) {
try
{
Object target = null;
if (System.getSecurityManager() == null) {
target = constructor.newInstance(args);
} else {
try {
target = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return constructor.newInstance(args);
}
});
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae);
}
}
return target;
}
catch (InstantiationException e)
{
throw new RuntimeException(Messages.MESSAGES.failedToConstruct(constructor.toString()), e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(Messages.MESSAGES.failedToConstruct(constructor.toString()), e);
}
catch (InvocationTargetException e)
{
throw new RuntimeException(Messages.MESSAGES.failedToConstruct(constructor.toString()), e.getCause());
}
catch (IllegalArgumentException e)
{
String msg = Messages.MESSAGES.badArguments(constructor.toString() + " (");
boolean first = false;
for (Object arg : args)
{
if (!first)
{
first = true;
}
else
{
msg += ",";
}
if (arg == null)
{
msg += " null";
continue;
}
msg += " " + arg;
}
throw new RuntimeException(msg, e);
}
}
}