Search in sources :

Example 1 with ReferenceCounted

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);
}
Also used : Disposable(reactor.core.Disposable) MutableHttpRequest(io.micronaut.http.MutableHttpRequest) ByteBuffer(io.micronaut.core.io.buffer.ByteBuffer) HttpClientException(io.micronaut.http.client.exceptions.HttpClientException) Subscriber(org.reactivestreams.Subscriber) JsonSubscriber(io.micronaut.http.netty.stream.JsonSubscriber) IdleStateEvent(io.netty.handler.timeout.IdleStateEvent) Event(io.micronaut.http.sse.Event) Subscription(org.reactivestreams.Subscription) ReferenceCounted(io.micronaut.core.io.buffer.ReferenceCounted)

Example 2 with ReferenceCounted

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);
    });
}
Also used : Publishers(io.micronaut.core.async.publisher.Publishers) ServerFilterChain(io.micronaut.http.filter.ServerFilterChain) BeanContext(io.micronaut.context.BeanContext) KotlinExecutableMethodUtils.isKotlinFunctionReturnTypeUnit(io.micronaut.inject.util.KotlinExecutableMethodUtils.isKotlinFunctionReturnTypeUnit) LoggerFactory(org.slf4j.LoggerFactory) HttpHeaders(io.micronaut.http.HttpHeaders) Internal(io.micronaut.core.annotation.Internal) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) HttpStatus(io.micronaut.http.HttpStatus) MediaType(io.micronaut.http.MediaType) ReferenceCounted(io.micronaut.core.io.buffer.ReferenceCounted) HttpResponse(io.micronaut.http.HttpResponse) MethodReference(io.micronaut.inject.MethodReference) MethodBasedRouteMatch(io.micronaut.web.router.MethodBasedRouteMatch) MutableHttpResponse(io.micronaut.http.MutableHttpResponse) Qualifiers(io.micronaut.inject.qualifiers.Qualifiers) Singleton(jakarta.inject.Singleton) MutableHttpHeaders(io.micronaut.http.MutableHttpHeaders) CompletionException(java.util.concurrent.CompletionException) HttpFilter(io.micronaut.http.filter.HttpFilter) RequestArgumentSatisfier(io.micronaut.http.server.binding.RequestArgumentSatisfier) List(java.util.List) RouteInfo(io.micronaut.web.router.RouteInfo) BeanCreationException(io.micronaut.context.exceptions.BeanCreationException) Optional(java.util.Optional) HttpAttributes(io.micronaut.http.HttpAttributes) Pattern(java.util.regex.Pattern) RouteMatch(io.micronaut.web.router.RouteMatch) UnsatisfiedRouteException(io.micronaut.web.router.exceptions.UnsatisfiedRouteException) LocalDateTime(java.time.LocalDateTime) CompletableFuture(java.util.concurrent.CompletableFuture) Scheduler(reactor.core.scheduler.Scheduler) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) Supplier(java.util.function.Supplier) ExceptionHandler(io.micronaut.http.server.exceptions.ExceptionHandler) ExecutableMethod(io.micronaut.inject.ExecutableMethod) ArrayList(java.util.ArrayList) ErrorContext(io.micronaut.http.server.exceptions.response.ErrorContext) Nullable(io.micronaut.core.annotation.Nullable) ReturnType(io.micronaut.core.type.ReturnType) Schedulers(reactor.core.scheduler.Schedulers) Argument(io.micronaut.core.type.Argument) HttpRequest(io.micronaut.http.HttpRequest) ServerRequestContext(io.micronaut.http.context.ServerRequestContext) ErrorResponseProcessor(io.micronaut.http.server.exceptions.response.ErrorResponseProcessor) ExecutorService(java.util.concurrent.ExecutorService) HttpStatusException(io.micronaut.http.exceptions.HttpStatusException) HttpMethod(io.micronaut.http.HttpMethod) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) ExecutorSelector(io.micronaut.scheduling.executor.ExecutorSelector) Publisher(org.reactivestreams.Publisher) Mono(reactor.core.publisher.Mono) IOException(java.io.IOException) BeanType(io.micronaut.inject.BeanType) ExecutionException(java.util.concurrent.ExecutionException) NonNull(io.micronaut.core.annotation.NonNull) Flux(reactor.core.publisher.Flux) ContinuationArgumentBinder(io.micronaut.http.bind.binders.ContinuationArgumentBinder) KotlinUtils.isKotlinCoroutineSuspended(io.micronaut.core.util.KotlinUtils.isKotlinCoroutineSuspended) BeanDefinition(io.micronaut.inject.BeanDefinition) Router(io.micronaut.web.router.Router) Collections(java.util.Collections) MutableHttpResponse(io.micronaut.http.MutableHttpResponse) Optional(java.util.Optional) Argument(io.micronaut.core.type.Argument) HttpStatus(io.micronaut.http.HttpStatus) MethodBasedRouteMatch(io.micronaut.web.router.MethodBasedRouteMatch) Mono(reactor.core.publisher.Mono) Flux(reactor.core.publisher.Flux) HttpResponse(io.micronaut.http.HttpResponse) MutableHttpResponse(io.micronaut.http.MutableHttpResponse) Publisher(org.reactivestreams.Publisher) MethodBasedRouteMatch(io.micronaut.web.router.MethodBasedRouteMatch) RouteMatch(io.micronaut.web.router.RouteMatch) Supplier(java.util.function.Supplier) ReferenceCounted(io.micronaut.core.io.buffer.ReferenceCounted)

Example 3 with ReferenceCounted

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));
}
Also used : FormattingTypeConverter(io.micronaut.core.convert.format.FormattingTypeConverter) java.util(java.util) Array(java.lang.reflect.Array) URL(java.net.URL) URISyntaxException(java.net.URISyntaxException) ArrayUtils(io.micronaut.core.util.ArrayUtils) MultiValuesConverterFactory(io.micronaut.core.convert.converters.MultiValuesConverterFactory) SimpleDateFormat(java.text.SimpleDateFormat) ConversionErrorException(io.micronaut.core.convert.exceptions.ConversionErrorException) Function(java.util.function.Function) ByteBuffer(java.nio.ByteBuffer) BigDecimal(java.math.BigDecimal) ConcurrentLinkedHashMap(io.micronaut.core.util.clhm.ConcurrentLinkedHashMap) Charset(java.nio.charset.Charset) ReferenceCounted(io.micronaut.core.io.buffer.ReferenceCounted) BigInteger(java.math.BigInteger) IllegalCharsetNameException(java.nio.charset.IllegalCharsetNameException) Argument(io.micronaut.core.type.Argument) URI(java.net.URI) NameUtils(io.micronaut.core.naming.NameUtils) ParseException(java.text.ParseException) Path(java.nio.file.Path) AnnotationClassValue(io.micronaut.core.annotation.AnnotationClassValue) ConvertibleValues(io.micronaut.core.convert.value.ConvertibleValues) ClassUtils(io.micronaut.core.reflect.ClassUtils) ConvertibleValuesMap(io.micronaut.core.convert.value.ConvertibleValuesMap) MalformedURLException(java.net.MalformedURLException) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) IOException(java.io.IOException) Reader(java.io.Reader) InputStreamReader(java.io.InputStreamReader) ReflectionUtils(io.micronaut.core.reflect.ReflectionUtils) File(java.io.File) StringUtils(io.micronaut.core.util.StringUtils) Format(io.micronaut.core.convert.format.Format) Paths(java.nio.file.Paths) CollectionUtils(io.micronaut.core.util.CollectionUtils) ReadableBytesTypeConverter(io.micronaut.core.convert.format.ReadableBytesTypeConverter) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) BufferedReader(java.io.BufferedReader) UnsupportedCharsetException(java.nio.charset.UnsupportedCharsetException) IOUtils(io.micronaut.core.io.IOUtils) InputStream(java.io.InputStream) MalformedURLException(java.net.MalformedURLException) ConcurrentLinkedHashMap(io.micronaut.core.util.clhm.ConcurrentLinkedHashMap) ReferenceCounted(io.micronaut.core.io.buffer.ReferenceCounted) MultiValuesConverterFactory(io.micronaut.core.convert.converters.MultiValuesConverterFactory) ReadableBytesTypeConverter(io.micronaut.core.convert.format.ReadableBytesTypeConverter) UnsupportedCharsetException(java.nio.charset.UnsupportedCharsetException) BigInteger(java.math.BigInteger) File(java.io.File) ConcurrentLinkedHashMap(io.micronaut.core.util.clhm.ConcurrentLinkedHashMap) ConvertibleValuesMap(io.micronaut.core.convert.value.ConvertibleValuesMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Argument(io.micronaut.core.type.Argument) URISyntaxException(java.net.URISyntaxException) ConversionErrorException(io.micronaut.core.convert.exceptions.ConversionErrorException) URI(java.net.URI) URL(java.net.URL) Function(java.util.function.Function) IllegalCharsetNameException(java.nio.charset.IllegalCharsetNameException) InputStreamReader(java.io.InputStreamReader) IOException(java.io.IOException) AnnotationClassValue(io.micronaut.core.annotation.AnnotationClassValue) URISyntaxException(java.net.URISyntaxException) ConversionErrorException(io.micronaut.core.convert.exceptions.ConversionErrorException) IllegalCharsetNameException(java.nio.charset.IllegalCharsetNameException) ParseException(java.text.ParseException) MalformedURLException(java.net.MalformedURLException) IOException(java.io.IOException) UnsupportedCharsetException(java.nio.charset.UnsupportedCharsetException) BigDecimal(java.math.BigDecimal) BigInteger(java.math.BigInteger) ConvertibleValues(io.micronaut.core.convert.value.ConvertibleValues) BufferedReader(java.io.BufferedReader) ParseException(java.text.ParseException) SimpleDateFormat(java.text.SimpleDateFormat) ConvertibleValuesMap(io.micronaut.core.convert.value.ConvertibleValuesMap)

Aggregations

ReferenceCounted (io.micronaut.core.io.buffer.ReferenceCounted)3 Argument (io.micronaut.core.type.Argument)2 IOException (java.io.IOException)2 Function (java.util.function.Function)2 BeanContext (io.micronaut.context.BeanContext)1 BeanCreationException (io.micronaut.context.exceptions.BeanCreationException)1 AnnotationClassValue (io.micronaut.core.annotation.AnnotationClassValue)1 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)1 Internal (io.micronaut.core.annotation.Internal)1 NonNull (io.micronaut.core.annotation.NonNull)1 Nullable (io.micronaut.core.annotation.Nullable)1 Publishers (io.micronaut.core.async.publisher.Publishers)1 MultiValuesConverterFactory (io.micronaut.core.convert.converters.MultiValuesConverterFactory)1 ConversionErrorException (io.micronaut.core.convert.exceptions.ConversionErrorException)1 Format (io.micronaut.core.convert.format.Format)1 FormattingTypeConverter (io.micronaut.core.convert.format.FormattingTypeConverter)1 ReadableBytesTypeConverter (io.micronaut.core.convert.format.ReadableBytesTypeConverter)1 ConvertibleValues (io.micronaut.core.convert.value.ConvertibleValues)1 ConvertibleValuesMap (io.micronaut.core.convert.value.ConvertibleValuesMap)1 IOUtils (io.micronaut.core.io.IOUtils)1