use of io.micronaut.core.io.buffer.ReferenceCounted in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method eventStreamOrError.
private <I> Publisher<Event<ByteBuffer<?>>> eventStreamOrError(@NonNull io.micronaut.http.HttpRequest<I> request, @NonNull Argument<?> errorType) {
if (request instanceof MutableHttpRequest) {
((MutableHttpRequest) request).accept(MediaType.TEXT_EVENT_STREAM_TYPE);
}
return Flux.create(emitter -> dataStream(request, errorType).subscribe(new Subscriber<ByteBuffer<?>>() {
private Subscription dataSubscription;
private CurrentEvent currentEvent;
@Override
public void onSubscribe(Subscription s) {
this.dataSubscription = s;
Disposable cancellable = () -> dataSubscription.cancel();
emitter.onCancel(cancellable);
if (!emitter.isCancelled() && emitter.requestedFromDownstream() > 0) {
// request the first chunk
dataSubscription.request(1);
}
}
@Override
public void onNext(ByteBuffer<?> buffer) {
try {
int len = buffer.readableBytes();
// emit the current event
if (len == 0) {
try {
Event event = Event.of(byteBufferFactory.wrap(currentEvent.data)).name(currentEvent.name).retry(currentEvent.retry).id(currentEvent.id);
emitter.next(event);
} finally {
currentEvent = null;
}
} else {
if (currentEvent == null) {
currentEvent = new CurrentEvent();
}
int colonIndex = buffer.indexOf((byte) ':');
// SSE comments start with colon, so skip
if (colonIndex > 0) {
// obtain the type
String type = buffer.slice(0, colonIndex).toString(StandardCharsets.UTF_8).trim();
int fromIndex = colonIndex + 1;
// skip the white space before the actual data
if (buffer.getByte(fromIndex) == ((byte) ' ')) {
fromIndex++;
}
if (fromIndex < len) {
int toIndex = len - fromIndex;
switch(type) {
case "data":
ByteBuffer content = buffer.slice(fromIndex, toIndex);
byte[] d = currentEvent.data;
if (d == null) {
currentEvent.data = content.toByteArray();
} else {
currentEvent.data = ArrayUtils.concat(d, content.toByteArray());
}
break;
case "id":
ByteBuffer id = buffer.slice(fromIndex, toIndex);
currentEvent.id = id.toString(StandardCharsets.UTF_8).trim();
break;
case "event":
ByteBuffer event = buffer.slice(fromIndex, toIndex);
currentEvent.name = event.toString(StandardCharsets.UTF_8).trim();
break;
case "retry":
ByteBuffer retry = buffer.slice(fromIndex, toIndex);
String text = retry.toString(StandardCharsets.UTF_8);
if (!StringUtils.isEmpty(text)) {
Long millis = Long.valueOf(text);
currentEvent.retry = Duration.ofMillis(millis);
}
break;
default:
// ignore message
break;
}
}
}
}
if (emitter.requestedFromDownstream() > 0 && !emitter.isCancelled()) {
dataSubscription.request(1);
}
} catch (Throwable e) {
onError(e);
} finally {
if (buffer instanceof ReferenceCounted) {
((ReferenceCounted) buffer).release();
}
}
}
@Override
public void onError(Throwable t) {
dataSubscription.cancel();
if (t instanceof HttpClientException) {
emitter.error(t);
} else {
emitter.error(new HttpClientException("Error consuming Server Sent Events: " + t.getMessage(), t));
}
}
@Override
public void onComplete() {
emitter.complete();
}
}), FluxSink.OverflowStrategy.BUFFER);
}
use of io.micronaut.core.io.buffer.ReferenceCounted in project micronaut-core by micronaut-projects.
the class RouteExecutor method createResponseForBody.
private Flux<MutableHttpResponse<?>> createResponseForBody(HttpRequest<?> request, Object body, RouteInfo<?> routeInfo) {
return Flux.<MutableHttpResponse<?>>defer(() -> {
Mono<MutableHttpResponse<?>> outgoingResponse;
if (body == null) {
if (routeInfo.isVoid()) {
MutableHttpResponse<Object> data = forStatus(routeInfo);
if (HttpMethod.permitsRequestBody(request.getMethod())) {
data.header(HttpHeaders.CONTENT_LENGTH, "0");
}
outgoingResponse = Mono.just(data);
} else {
outgoingResponse = Mono.just(newNotFoundError(request));
}
} else {
HttpStatus defaultHttpStatus = routeInfo.isErrorRoute() ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.OK;
boolean isReactive = routeInfo.isAsyncOrReactive() || Publishers.isConvertibleToPublisher(body);
if (isReactive) {
Class<?> bodyClass = body.getClass();
boolean isSingle = isSingle(routeInfo, bodyClass);
boolean isCompletable = !isSingle && routeInfo.isVoid() && Publishers.isCompletable(bodyClass);
if (isSingle || isCompletable) {
// full response case
Publisher<Object> publisher = Publishers.convertPublisher(body, Publisher.class);
Supplier<MutableHttpResponse<?>> emptyResponse = () -> {
MutableHttpResponse<?> singleResponse;
if (isCompletable || routeInfo.isVoid()) {
singleResponse = forStatus(routeInfo, HttpStatus.OK).header(HttpHeaders.CONTENT_LENGTH, "0");
} else {
singleResponse = newNotFoundError(request);
}
return singleResponse;
};
return Flux.from(publisher).flatMap(o -> {
MutableHttpResponse<?> singleResponse;
if (o instanceof Optional) {
Optional optional = (Optional) o;
if (optional.isPresent()) {
o = ((Optional<?>) o).get();
} else {
return Flux.just(emptyResponse.get());
}
}
if (o instanceof HttpResponse) {
singleResponse = toMutableResponse((HttpResponse<?>) o);
final Argument<?> bodyArgument = // Mono
routeInfo.getReturnType().getFirstTypeVariable().orElse(// HttpResponse
Argument.OBJECT_ARGUMENT).getFirstTypeVariable().orElse(// Mono
Argument.OBJECT_ARGUMENT);
if (bodyArgument.isAsyncOrReactive()) {
return processPublisherBody(request, singleResponse, routeInfo);
}
} else if (o instanceof HttpStatus) {
singleResponse = forStatus(routeInfo, (HttpStatus) o);
} else {
singleResponse = forStatus(routeInfo, defaultHttpStatus).body(o);
}
return Flux.just(singleResponse);
}).switchIfEmpty(Mono.fromSupplier(emptyResponse));
} else {
// streaming case
Argument<?> typeArgument = routeInfo.getReturnType().getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
if (HttpResponse.class.isAssignableFrom(typeArgument.getType())) {
// a response stream
Publisher<HttpResponse<?>> bodyPublisher = Publishers.convertPublisher(body, Publisher.class);
Flux<MutableHttpResponse<?>> response = Flux.from(bodyPublisher).map(this::toMutableResponse);
Argument<?> bodyArgument = typeArgument.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
if (bodyArgument.isAsyncOrReactive()) {
return response.flatMap((resp) -> processPublisherBody(request, resp, routeInfo));
}
return response;
} else {
MutableHttpResponse<?> response = forStatus(routeInfo, defaultHttpStatus).body(body);
return processPublisherBody(request, response, routeInfo);
}
}
}
// now we have the raw result, transform it as necessary
if (body instanceof HttpStatus) {
outgoingResponse = Mono.just(HttpResponse.status((HttpStatus) body));
} else {
if (routeInfo.isSuspended()) {
boolean isKotlinFunctionReturnTypeUnit = routeInfo instanceof MethodBasedRouteMatch && isKotlinFunctionReturnTypeUnit(((MethodBasedRouteMatch) routeInfo).getExecutableMethod());
final Supplier<CompletableFuture<?>> supplier = ContinuationArgumentBinder.extractContinuationCompletableFutureSupplier(request);
if (isKotlinCoroutineSuspended(body)) {
return Mono.fromCompletionStage(supplier).<MutableHttpResponse<?>>flatMap(obj -> {
MutableHttpResponse<?> response;
if (obj instanceof HttpResponse) {
response = toMutableResponse((HttpResponse<?>) obj);
final Argument<?> bodyArgument = routeInfo.getReturnType().getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
if (bodyArgument.isAsyncOrReactive()) {
return processPublisherBody(request, response, routeInfo);
}
} else {
response = forStatus(routeInfo, defaultHttpStatus);
if (!isKotlinFunctionReturnTypeUnit) {
response = response.body(obj);
}
}
return Mono.just(response);
}).switchIfEmpty(createNotFoundErrorResponsePublisher(request));
} else {
Object suspendedBody;
if (isKotlinFunctionReturnTypeUnit) {
suspendedBody = Mono.empty();
} else {
suspendedBody = body;
}
outgoingResponse = toMutableResponse(request, routeInfo, defaultHttpStatus, suspendedBody);
}
} else {
outgoingResponse = toMutableResponse(request, routeInfo, defaultHttpStatus, body);
}
}
}
// for head request we never emit the body
if (request != null && request.getMethod().equals(HttpMethod.HEAD)) {
outgoingResponse = outgoingResponse.map(r -> {
final Object o = r.getBody().orElse(null);
if (o instanceof ReferenceCounted) {
((ReferenceCounted) o).release();
}
r.body(null);
return r;
});
}
return outgoingResponse;
}).doOnNext((response) -> {
applyConfiguredHeaders(response.getHeaders());
if (routeInfo instanceof RouteMatch) {
response.setAttribute(HttpAttributes.ROUTE_MATCH, routeInfo);
}
response.setAttribute(HttpAttributes.ROUTE_INFO, routeInfo);
});
}
use of io.micronaut.core.io.buffer.ReferenceCounted in project micronaut-core by micronaut-projects.
the class DefaultConversionService method registerDefaultConverters.
/**
* Default Converters.
*/
@SuppressWarnings({ "OptionalIsPresent", "unchecked" })
protected void registerDefaultConverters() {
// primitive array to wrapper array
@SuppressWarnings("rawtypes") Function primitiveArrayToWrapperArray = ArrayUtils::toWrapperArray;
addConverter(double[].class, Double[].class, primitiveArrayToWrapperArray);
addConverter(byte[].class, Byte[].class, primitiveArrayToWrapperArray);
addConverter(short[].class, Short[].class, primitiveArrayToWrapperArray);
addConverter(boolean[].class, Boolean[].class, primitiveArrayToWrapperArray);
addConverter(int[].class, Integer[].class, primitiveArrayToWrapperArray);
addConverter(float[].class, Float[].class, primitiveArrayToWrapperArray);
addConverter(double[].class, Double[].class, primitiveArrayToWrapperArray);
addConverter(char[].class, Character[].class, primitiveArrayToWrapperArray);
// wrapper to primitive array converters
Function<Object[], Object> wrapperArrayToPrimitiveArray = ArrayUtils::toPrimitiveArray;
// noinspection rawtypes
addConverter(Double[].class, double[].class, (Function) wrapperArrayToPrimitiveArray);
// noinspection rawtypes
addConverter(Integer[].class, int[].class, (Function) wrapperArrayToPrimitiveArray);
// Object -> List
addConverter(Object.class, List.class, (object, targetType, context) -> {
Optional<Argument<?>> firstTypeVariable = context.getFirstTypeVariable();
Argument<?> argument = firstTypeVariable.orElse(Argument.OBJECT_ARGUMENT);
Optional converted = DefaultConversionService.this.convert(object, context.with(argument));
if (converted.isPresent()) {
return Optional.of(Collections.singletonList(converted.get()));
}
return Optional.empty();
});
// String -> Class
addConverter(CharSequence.class, Class.class, (object, targetType, context) -> {
ClassLoader classLoader = targetType.getClassLoader();
if (classLoader == null) {
classLoader = DefaultConversionService.class.getClassLoader();
}
return ClassUtils.forName(object.toString(), classLoader);
});
// AnnotationClassValue -> Class
addConverter(AnnotationClassValue.class, Class.class, (object, targetType, context) -> object.getType());
addConverter(AnnotationClassValue.class, Object.class, (object, targetType, context) -> {
if (targetType.equals(Class.class)) {
return object.getType();
} else {
if (CharSequence.class.isAssignableFrom(targetType)) {
return Optional.of(object.getName());
} else {
Optional i = object.getInstance();
if (i.isPresent() && targetType.isInstance(i.get())) {
return i;
}
return Optional.empty();
}
}
});
addConverter(AnnotationClassValue[].class, Class.class, (object, targetType, context) -> {
if (object.length > 0) {
final AnnotationClassValue o = object[0];
if (o != null) {
return o.getType();
}
}
return Optional.empty();
});
addConverter(AnnotationClassValue[].class, Class[].class, (object, targetType, context) -> {
List<Class> classes = new ArrayList<>(object.length);
for (AnnotationClassValue<?> annotationClassValue : object) {
if (annotationClassValue != null) {
final Optional<? extends Class<?>> type = annotationClassValue.getType();
if (type.isPresent()) {
classes.add(type.get());
}
}
}
return Optional.of(classes.toArray(new Class[0]));
});
// URI -> URL
addConverter(URI.class, URL.class, uri -> {
try {
return uri.toURL();
} catch (MalformedURLException e) {
return null;
}
});
// InputStream -> String
addConverter(InputStream.class, String.class, (object, targetType, context) -> {
BufferedReader reader = new BufferedReader(new InputStreamReader(object));
try {
return Optional.of(IOUtils.readText(reader));
} catch (IOException e) {
context.reject(e);
return Optional.empty();
}
});
// String -> byte[]
addConverter(CharSequence.class, byte[].class, (object, targetType, context) -> Optional.of(object.toString().getBytes(context.getCharset())));
addConverter(Integer.class, byte[].class, (object, targetType, context) -> Optional.of(ByteBuffer.allocate(Integer.BYTES).putInt(object).array()));
addConverter(Character.class, byte[].class, (object, targetType, context) -> Optional.of(ByteBuffer.allocate(Integer.BYTES).putChar(object).array()));
addConverter(Long.class, byte[].class, (object, targetType, context) -> Optional.of(ByteBuffer.allocate(Long.BYTES).putLong(object).array()));
addConverter(Short.class, byte[].class, (object, targetType, context) -> Optional.of(ByteBuffer.allocate(Short.BYTES).putShort(object).array()));
addConverter(Double.class, byte[].class, (object, targetType, context) -> Optional.of(ByteBuffer.allocate(Double.BYTES).putDouble(object).array()));
addConverter(Float.class, byte[].class, (object, targetType, context) -> Optional.of(ByteBuffer.allocate(Float.BYTES).putFloat(object).array()));
// InputStream -> Number
addConverter(InputStream.class, Number.class, (object, targetType, context) -> {
Optional<String> convert = DefaultConversionService.this.convert(object, String.class, context);
if (convert.isPresent()) {
return convert.flatMap(val -> DefaultConversionService.this.convert(val, targetType, context));
}
return Optional.empty();
});
// Reader -> String
addConverter(Reader.class, String.class, (object, targetType, context) -> {
BufferedReader reader = object instanceof BufferedReader ? (BufferedReader) object : new BufferedReader(object);
try {
return Optional.of(IOUtils.readText(reader));
} catch (IOException e) {
context.reject(e);
return Optional.empty();
}
});
// String -> File
addConverter(CharSequence.class, File.class, (object, targetType, context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
return Optional.of(new File(object.toString()));
});
// String[] -> Enum
addConverter(String[].class, Enum.class, (object, targetType, context) -> {
if (object == null || object.length == 0) {
return Optional.empty();
}
StringJoiner joiner = new StringJoiner("");
for (String string : object) {
joiner.add(string);
}
final String val = joiner.toString();
return convert(val, targetType, context);
});
addConverter(String[].class, CharSequence.class, (object, targetType, context) -> {
if (object == null || object.length == 0) {
return Optional.empty();
}
StringJoiner joiner = new StringJoiner("");
for (String string : object) {
joiner.add(string);
}
return convert(joiner.toString(), targetType, context);
});
// CharSequence -> Long for bytes
addConverter(CharSequence.class, Number.class, new ReadableBytesTypeConverter());
// CharSequence -> Date
addConverter(CharSequence.class, Date.class, (object, targetType, context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
SimpleDateFormat format = resolveFormat(context);
return Optional.of(format.parse(object.toString()));
} catch (ParseException e) {
context.reject(object, e);
return Optional.empty();
}
});
// Date -> CharSequence
addConverter(Date.class, CharSequence.class, (object, targetType, context) -> {
SimpleDateFormat format = resolveFormat(context);
return Optional.of(format.format(object));
});
// String -> Path
addConverter(CharSequence.class, Path.class, (object, targetType, context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
return Optional.ofNullable(Paths.get(object.toString()));
} catch (Exception e) {
context.reject("Invalid path [" + object + " ]: " + e.getMessage(), e);
return Optional.empty();
}
});
// String -> Integer
addConverter(CharSequence.class, Integer.class, (CharSequence object, Class<Integer> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
Integer converted = Integer.valueOf(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> BigInteger
addConverter(CharSequence.class, BigInteger.class, (CharSequence object, Class<BigInteger> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
BigInteger converted = new BigInteger(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Float
addConverter(CharSequence.class, Float.class, (CharSequence object, Class<Float> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
Float converted = Float.valueOf(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Double
addConverter(CharSequence.class, Double.class, (CharSequence object, Class<Double> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
Double converted = Double.valueOf(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Long
addConverter(CharSequence.class, Long.class, (CharSequence object, Class<Long> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
Long converted = Long.valueOf(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Short
addConverter(CharSequence.class, Short.class, (CharSequence object, Class<Short> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
Short converted = Short.valueOf(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Byte
addConverter(CharSequence.class, Byte.class, (CharSequence object, Class<Byte> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
Byte converted = Byte.valueOf(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> BigDecimal
addConverter(CharSequence.class, BigDecimal.class, (CharSequence object, Class<BigDecimal> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
BigDecimal converted = new BigDecimal(object.toString());
return Optional.of(converted);
} catch (NumberFormatException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Boolean
addConverter(CharSequence.class, Boolean.class, (CharSequence object, Class<Boolean> targetType, ConversionContext context) -> {
String booleanString = object.toString().toLowerCase(Locale.ENGLISH);
switch(booleanString) {
case "yes":
case "y":
case "on":
case "true":
return Optional.of(Boolean.TRUE);
default:
return Optional.of(Boolean.FALSE);
}
});
// String -> URL
addConverter(CharSequence.class, URL.class, (CharSequence object, Class<URL> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
String spec = object.toString();
if (!spec.contains("://")) {
spec = "http://" + spec;
}
return Optional.of(new URL(spec));
} catch (MalformedURLException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> URI
addConverter(CharSequence.class, URI.class, (CharSequence object, Class<URI> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
return Optional.of(new URI(object.toString()));
} catch (URISyntaxException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Locale
addConverter(CharSequence.class, Locale.class, object -> StringUtils.parseLocale(object.toString()));
// String -> UUID
addConverter(CharSequence.class, UUID.class, (CharSequence object, Class<UUID> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
return Optional.of(UUID.fromString(object.toString()));
} catch (IllegalArgumentException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Currency
addConverter(CharSequence.class, Currency.class, (CharSequence object, Class<Currency> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
return Optional.of(Currency.getInstance(object.toString()));
} catch (IllegalArgumentException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> TimeZone
addConverter(CharSequence.class, TimeZone.class, (CharSequence object, Class<TimeZone> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
return Optional.of(TimeZone.getTimeZone(object.toString()));
});
// String -> Charset
addConverter(CharSequence.class, Charset.class, (CharSequence object, Class<Charset> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
try {
return Optional.of(Charset.forName(object.toString()));
} catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
context.reject(object, e);
return Optional.empty();
}
});
// String -> Character
addConverter(CharSequence.class, Character.class, (CharSequence object, Class<Character> targetType, ConversionContext context) -> {
String str = object.toString();
if (str.length() == 1) {
return Optional.of(str.charAt(0));
} else {
return Optional.empty();
}
});
// String -> Array
addConverter(CharSequence.class, Object[].class, (CharSequence object, Class<Object[]> targetType, ConversionContext context) -> {
if (object instanceof AnnotationClassValue && targetType.equals(AnnotationClassValue[].class)) {
AnnotationClassValue[] array = new AnnotationClassValue[1];
array[0] = (AnnotationClassValue) object;
return Optional.of(array);
}
String str = object.toString();
String[] strings = str.split(",");
Class<?> componentType = ReflectionUtils.getWrapperType(targetType.getComponentType());
Object newArray = Array.newInstance(componentType, strings.length);
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
Optional<?> converted = convert(string, componentType);
if (converted.isPresent()) {
Array.set(newArray, i, converted.get());
}
}
return Optional.of((Object[]) newArray);
});
// String -> Int Array
addConverter(CharSequence.class, int[].class, (CharSequence object, Class<int[]> targetType, ConversionContext context) -> {
String str = object.toString();
String[] strings = str.split(",");
Object newArray = Array.newInstance(int.class, strings.length);
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
Optional<?> converted = convert(string, int.class);
if (converted.isPresent()) {
Array.set(newArray, i, converted.get());
}
}
return Optional.of((int[]) newArray);
});
// String -> Char Array
addConverter(String.class, char[].class, (String object, Class<char[]> targetType, ConversionContext context) -> Optional.of(object.toCharArray()));
// Object[] -> String[]
addConverter(Object[].class, String[].class, (Object[] object, Class<String[]> targetType, ConversionContext context) -> {
String[] strings = new String[object.length];
for (int i = 0; i < object.length; i++) {
Object o = object[i];
if (o != null) {
strings[i] = o.toString();
}
}
return Optional.of(strings);
});
// String -> Enum
addConverter(CharSequence.class, Enum.class, (CharSequence object, Class<Enum> targetType, ConversionContext context) -> {
if (StringUtils.isEmpty(object)) {
return Optional.empty();
}
String stringValue = object.toString();
try {
Enum val = Enum.valueOf(targetType, stringValue);
return Optional.of(val);
} catch (IllegalArgumentException e) {
try {
Enum val = Enum.valueOf(targetType, NameUtils.environmentName(stringValue));
return Optional.of(val);
} catch (Exception e1) {
Optional<Enum> valOpt = Arrays.stream(targetType.getEnumConstants()).filter(val -> val.toString().equals(stringValue)).findFirst();
if (valOpt.isPresent()) {
return valOpt;
}
context.reject(object, e);
return Optional.empty();
}
}
});
// Object -> String
addConverter(Object.class, String.class, (Object object, Class<String> targetType, ConversionContext context) -> Optional.of(object.toString()));
// Number -> Number
addConverter(Number.class, Number.class, (Number object, Class<Number> targetType, ConversionContext context) -> {
Class targetNumberType = ReflectionUtils.getWrapperType(targetType);
if (targetNumberType.isInstance(object)) {
return Optional.of(object);
}
if (targetNumberType == Integer.class) {
return Optional.of(object.intValue());
}
if (targetNumberType == Long.class) {
return Optional.of(object.longValue());
}
if (targetNumberType == Short.class) {
return Optional.of(object.shortValue());
}
if (targetNumberType == Byte.class) {
return Optional.of(object.byteValue());
}
if (targetNumberType == Float.class) {
return Optional.of(object.floatValue());
}
if (targetNumberType == Double.class) {
return Optional.of(object.doubleValue());
}
if (targetNumberType == BigInteger.class) {
if (object instanceof BigDecimal) {
return Optional.of(((BigDecimal) object).toBigInteger());
}
return Optional.of(BigInteger.valueOf(object.longValue()));
}
if (targetNumberType == BigDecimal.class) {
return Optional.of(new BigDecimal(object.toString()));
}
return Optional.empty();
});
// String -> List/Iterable
addConverter(CharSequence.class, Iterable.class, (CharSequence object, Class<Iterable> targetType, ConversionContext context) -> {
Optional<Argument<?>> typeVariable = context.getFirstTypeVariable();
Argument<?> componentType = typeVariable.orElse(Argument.OBJECT_ARGUMENT);
ConversionContext newContext = context.with(componentType);
Class<?> targetComponentType = ReflectionUtils.getWrapperType(componentType.getType());
String[] strings = object.toString().split(",");
List list = new ArrayList();
for (String string : strings) {
Optional converted = convert(string, targetComponentType, newContext);
if (converted.isPresent()) {
list.add(converted.get());
}
}
return CollectionUtils.convertCollection((Class) targetType, list);
});
TypeConverter<Object, Optional> objectToOptionalConverter = (object, targetType, context) -> {
Optional<Argument<?>> typeVariable = context.getFirstTypeVariable();
Argument<?> componentType = typeVariable.orElse(Argument.OBJECT_ARGUMENT);
Class<?> targetComponentType = ReflectionUtils.getWrapperType(componentType.getType());
ConversionContext newContext = context.with(componentType).with(context.getAnnotationMetadata());
Optional converted = convert(object, targetComponentType, newContext);
if (converted.isPresent()) {
return Optional.of(converted);
}
return Optional.of(Optional.empty());
};
// Optional handling
addConverter(Object.class, Optional.class, objectToOptionalConverter);
addConverter(Object.class, OptionalInt.class, (object, targetType, context) -> {
Optional<Integer> converted = convert(object, Integer.class, context);
return converted.map(integer -> Optional.of(OptionalInt.of(integer))).orElseGet(() -> Optional.of(OptionalInt.empty()));
});
addConverter(Object.class, OptionalLong.class, (object, targetType, context) -> {
Optional<Long> converted = convert(object, Long.class, context);
return converted.map(longValue -> Optional.of(OptionalLong.of(longValue))).orElseGet(() -> Optional.of(OptionalLong.empty()));
});
// Iterable -> String
addConverter(Iterable.class, String.class, (object, targetType, context) -> Optional.of(CollectionUtils.toString(object)));
// Iterable -> Object
addConverter(Iterable.class, Object.class, (object, targetType, context) -> {
if (Optional.class.isAssignableFrom(targetType)) {
return objectToOptionalConverter.convert(object, (Class) targetType, context);
}
Iterator<?> i = object.iterator();
int count = 0;
Object value = null;
while (i.hasNext()) {
if (count > 0) {
context.reject(object, new ConversionErrorException(Argument.of(targetType), new IllegalArgumentException("Cannot convert an iterable with more than 1 value to a non collection object")));
return Optional.empty();
}
count++;
value = i.next();
}
return convert(value, targetType, context);
});
// Iterable -> Iterable (inner type conversion)
addConverter(Iterable.class, Iterable.class, (object, targetType, context) -> {
if (ConvertibleValues.class.isAssignableFrom(targetType)) {
if (object instanceof ConvertibleValues) {
return Optional.of(object);
}
return Optional.empty();
}
Optional<Argument<?>> typeVariable = context.getFirstTypeVariable();
Argument<?> componentType = typeVariable.orElse(Argument.OBJECT_ARGUMENT);
Class<?> targetComponentType = ReflectionUtils.getWrapperType(componentType.getType());
if (targetType.isInstance(object) && targetComponentType == Object.class) {
return Optional.of(object);
}
List list = new ArrayList();
ConversionContext newContext = context.with(componentType);
for (Object o : object) {
Optional<?> converted = convert(o, targetComponentType, newContext);
if (converted.isPresent()) {
list.add(converted.get());
}
}
return CollectionUtils.convertCollection((Class) targetType, list);
});
// Object[] -> String
addConverter(Object[].class, String.class, (object, targetType, context) -> Optional.of(ArrayUtils.toString(object)));
// Object[] -> Object[] (inner type conversion)
addConverter(Object[].class, Object[].class, (object, targetType, context) -> {
Class<?> targetComponentType = targetType.getComponentType();
List results = new ArrayList();
for (Object o : object) {
Optional<?> converted = convert(o, targetComponentType, context);
if (converted.isPresent()) {
results.add(converted.get());
}
}
return Optional.of(results.toArray((Object[]) Array.newInstance(targetComponentType, results.size())));
});
// Iterable -> Object[]
addConverter(Iterable.class, Object[].class, (object, targetType, context) -> {
Class<?> targetComponentType = targetType.getComponentType();
List results = new ArrayList();
for (Object o : object) {
Optional<?> converted = convert(o, targetComponentType, context);
if (converted.isPresent()) {
results.add(converted.get());
}
}
return Optional.of(results.toArray((Object[]) Array.newInstance(targetComponentType, results.size())));
});
addConverter(Object[].class, Iterable.class, (object, targetType, context) -> convert(Arrays.asList(object), targetType, context));
addConverter(Object.class, Object[].class, (object, targetType, context) -> {
Class<?> targetComponentType = targetType.getComponentType();
Optional<?> converted = convert(object, targetComponentType);
if (converted.isPresent()) {
Object[] result = (Object[]) Array.newInstance(targetComponentType, 1);
result[0] = converted.get();
return Optional.of(result);
}
return Optional.empty();
});
// Map -> Map (inner type conversion)
addConverter(Map.class, Map.class, (object, targetType, context) -> {
Argument<?> keyArgument = context.getTypeVariable("K").orElse(Argument.of(String.class, "K"));
boolean isProperties = targetType.equals(Properties.class);
Argument<?> valArgument = context.getTypeVariable("V").orElseGet(() -> {
if (isProperties) {
return Argument.of(String.class, "V");
}
return Argument.of(Object.class, "V");
});
Class keyType = keyArgument.getType();
Class valueType = valArgument.getType();
ConversionContext keyContext = context.with(keyArgument);
ConversionContext valContext = context.with(valArgument);
Map newMap = isProperties ? new Properties() : new LinkedHashMap();
for (Object o : object.entrySet()) {
Map.Entry entry = (Map.Entry) o;
Object key = entry.getKey();
Object value = entry.getValue();
if (!keyType.isInstance(key)) {
Optional convertedKey = convert(key, keyType, keyContext);
if (convertedKey.isPresent()) {
key = convertedKey.get();
} else {
continue;
}
}
if (!valueType.isInstance(value) || value instanceof Map || value instanceof Collection) {
Optional converted = convert(value, valueType, valContext);
if (converted.isPresent()) {
value = converted.get();
} else {
continue;
}
}
newMap.put(key, value);
}
return Optional.of(newMap);
});
addConverter(Map.class, ConvertibleValues.class, (object, targetType, context) -> Optional.of(new ConvertibleValuesMap<Object>(object)));
// Micronaut ByteBuffer -> byte for streamed results from HTTP clients
addConverter(io.micronaut.core.io.buffer.ByteBuffer.class, byte[].class, (object, targetType, context) -> {
byte[] result = object.toByteArray();
((ReferenceCounted) object).release();
return Optional.of(result);
});
// ConvertibleMultiValues -> [?]
addConverter(io.micronaut.core.convert.value.ConvertibleMultiValues.class, Iterable.class, new MultiValuesConverterFactory.MultiValuesToIterableConverter(this));
addConverter(io.micronaut.core.convert.value.ConvertibleMultiValues.class, Map.class, new MultiValuesConverterFactory.MultiValuesToMapConverter(this));
addConverter(io.micronaut.core.convert.value.ConvertibleMultiValues.class, Object.class, new MultiValuesConverterFactory.MultiValuesToObjectConverter(this));
// [?] -> ConvertibleMultiValues
addConverter(Iterable.class, io.micronaut.core.convert.value.ConvertibleMultiValues.class, new MultiValuesConverterFactory.IterableToMultiValuesConverter(this));
addConverter(Map.class, io.micronaut.core.convert.value.ConvertibleMultiValues.class, new MultiValuesConverterFactory.MapToMultiValuesConverter(this));
addConverter(Object.class, io.micronaut.core.convert.value.ConvertibleMultiValues.class, new MultiValuesConverterFactory.ObjectToMultiValuesConverter(this));
}
Aggregations