/** * */ package io.sinistral.proteus.server; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.google.inject.Inject; import io.sinistral.proteus.server.predicates.ServerPredicates; import io.undertow.server.HttpServerExchange; import io.undertow.server.handlers.form.FormDataParser; import io.undertow.util.HttpString; import io.undertow.util.Methods; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Date; import java.util.Deque; import java.util.Objects; import java.util.function.Function; /** * @author jbauer */ public class Extractors { private static Logger log = LoggerFactory.getLogger(Extractors.class.getCanonicalName()); @Inject private static XmlMapper XML_MAPPER; @Inject private static ObjectMapper OBJECT_MAPPER; private static JsonNode parseJson(byte[] bytes) { try { return OBJECT_MAPPER.readTree(bytes); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } public static class Optional { public static <T> java.util.Optional<T> extractWithFunction(final HttpServerExchange exchange, final String name, Function<String, T> function) { return string(exchange, name).map(function); } public static java.util.Optional<JsonNode> jsonNode(final HttpServerExchange exchange) { return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(ByteBuffer::array).map(o -> parseJson(o)); } public static <T> java.util.Optional<T> model(final HttpServerExchange exchange, final TypeReference<T> type) { if (ServerPredicates.XML_PREDICATE.resolve(exchange)) { return xmlModel(exchange, type); } else { return jsonModel(exchange, type); } } public static <T> java.util.Optional<T> model(final HttpServerExchange exchange, final Class<T> type) { if (ServerPredicates.XML_PREDICATE.resolve(exchange)) { return xmlModel(exchange, type); } else { return jsonModel(exchange, type); } } public static <T> java.util.Optional<T> jsonModel(final HttpServerExchange exchange, final TypeReference<T> type) { return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(ByteBuffer::array).map(b -> { try { return OBJECT_MAPPER.readValue(b, type); } catch (Exception e) { log.error(e.getMessage(), e); return null; } }); } public static <T> java.util.Optional<T> jsonModel(final HttpServerExchange exchange, final Class<T> type) { return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(ByteBuffer::array).map(b -> { try { return OBJECT_MAPPER.readValue(b, type); } catch (Exception e) { log.error(e.getMessage(), e); return null; } }); } public static <T> java.util.Optional<T> xmlModel(final HttpServerExchange exchange, final TypeReference<T> type) { return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(ByteBuffer::array).map(b -> { try { return XML_MAPPER.readValue(b, XML_MAPPER.getTypeFactory().constructType(type.getType())); } catch (Exception e) { log.error(e.getMessage(), e); return null; } }); } public static <T> java.util.Optional<T> xmlModel(final HttpServerExchange exchange, final Class<T> type) { return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(ByteBuffer::array).map(b -> { try { return XML_MAPPER.readValue(b, type); } catch (Exception e) { log.error(e.getMessage(), e); return null; } }); } public static java.util.Optional<Date> date(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(OffsetDateTime::parse).map(OffsetDateTime::toInstant).map(Date::from); } public static java.util.Optional<OffsetDateTime> offsetDateTime(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(OffsetDateTime::parse); } public static java.util.Optional<ZonedDateTime> zonedDateTime(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(ZonedDateTime::parse); } public static java.util.Optional<Instant> instant(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(Instant::parse); } public static java.util.Optional<JsonNode> any(final HttpServerExchange exchange) { return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(b -> parseJson(b.array())); } public static java.util.Optional<Integer> integerValue(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(Integer::parseInt); } public static java.util.Optional<Short> shortValue(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(Short::parseShort); } public static java.util.Optional<Float> floatValue(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(Float::parseFloat); } public static java.util.Optional<Double> doubleValue(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(Double::parseDouble); } public static java.util.Optional<BigDecimal> bigDecimalValue(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(BigDecimal::new); } public static java.util.Optional<Long> longValue(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(Long::parseLong); } public static java.util.Optional<Boolean> booleanValue(final HttpServerExchange exchange, final String name) { return string(exchange, name).map(Boolean::parseBoolean); } // public static <E extends Enum<E>> java.util.Optional<E> enumValue(final HttpServerExchange exchange, final Class<E> clazz, final String name) // { // return string(exchange, name).map(e -> Enum.valueOf(clazz, name)); // } public static java.util.Optional<String> string(final HttpServerExchange exchange, final String name) { return java.util.Optional.ofNullable(exchange.getQueryParameters().get(name)).map(Deque::getFirst); } public static java.util.Optional<Path> filePath(final HttpServerExchange exchange, final String name) { return java.util.Optional.ofNullable(exchange.getAttachment(FormDataParser.FORM_DATA).get(name)).map(Deque::getFirst).map(fv -> fv.getFileItem().getFile()); } public static java.util.Optional<File> file(final HttpServerExchange exchange, final String name) { return java.util.Optional.ofNullable(exchange.getAttachment(FormDataParser.FORM_DATA).get(name)).map(Deque::getFirst).map(fv -> fv.getFileItem().getFile().toFile()); } public static java.util.Optional<ByteBuffer> byteBuffer(final HttpServerExchange exchange, final String name) { return Optional.filePath(exchange, name).map(fp -> { try (final FileChannel fileChannel = FileChannel.open(fp, StandardOpenOption.READ)) { final ByteBuffer buffer = ByteBuffer.allocate((int) fileChannel.size()); fileChannel.read(buffer); buffer.flip(); return buffer; } catch (Exception e) { return null; } }); } } public static class Header { public static String string(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { try { return exchange.getRequestHeaders().get(name).getFirst(); } catch (NullPointerException e) { throw new IllegalArgumentException("Missing parameter " + name, e); } } public static class Optional { public static java.util.Optional<String> string(final HttpServerExchange exchange, final String name) { return java.util.Optional.ofNullable(exchange.getRequestHeaders().get(name)).map(Deque::getFirst); } } } public static Date date(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Date.from(OffsetDateTime.parse(string(exchange, name)).toInstant()); } public static ZonedDateTime zonedDateTime(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return ZonedDateTime.parse(string(exchange, name)); } public static OffsetDateTime offsetDateTime(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return OffsetDateTime.parse(string(exchange, name)); } public static <T> T jsonModel(final HttpServerExchange exchange, final TypeReference<T> type) throws IllegalArgumentException, IOException { final byte[] attachment = exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array(); return OBJECT_MAPPER.readValue(attachment, type); } public static <T> T jsonModel(final HttpServerExchange exchange, final Class<T> type) throws IllegalArgumentException, IOException { final byte[] attachment = exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array(); return OBJECT_MAPPER.readValue(attachment, type); } public static <T> T xmlModel(final HttpServerExchange exchange, final Class<T> type) throws IllegalArgumentException, IOException { final byte[] attachment = exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array(); return XML_MAPPER.readValue(attachment, type); } public static <T> T xmlModel(final HttpServerExchange exchange, final TypeReference<T> type) throws IllegalArgumentException, IOException { final byte[] attachment = exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array(); return XML_MAPPER.readValue(attachment, XML_MAPPER.getTypeFactory().constructType(type.getType())); } public static JsonNode any(final HttpServerExchange exchange) { try { return parseJson(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array()); } catch (Exception e) { log.warn(e.getMessage(), e); return OBJECT_MAPPER.createObjectNode(); } } public static JsonNode jsonNode(final HttpServerExchange exchange) { return parseJson(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array()); } public static Path filePath(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { try { return exchange.getAttachment(FormDataParser.FORM_DATA).get(name).getFirst().getFileItem().getFile(); } catch (NullPointerException e) { throw new IllegalArgumentException("Missing parameter " + name, e); } } public static File file(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { try { return exchange.getAttachment(FormDataParser.FORM_DATA).get(name).getFirst().getFileItem().getFile().toFile(); } catch (NullPointerException e) { throw new IllegalArgumentException("Missing parameter " + name, e); } } public static ByteBuffer byteBuffer(final HttpServerExchange exchange, final String name) throws IOException { final Path filePath = filePath(exchange, name); try (final FileChannel fileChannel = FileChannel.open(filePath, StandardOpenOption.READ)) { final ByteBuffer buffer = ByteBuffer.allocate((int) fileChannel.size()); fileChannel.read(buffer); buffer.flip(); return buffer; } } public static String string(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { try { return exchange.getQueryParameters().get(name).getFirst(); } catch (NullPointerException e) { throw new IllegalArgumentException("Missing parameter " + name, e); } } public static <T> T extractWithFunction(final HttpServerExchange exchange, final String name, Function<String, T> function) throws IllegalArgumentException { return function.apply(string(exchange, name)); } public static Float floatValue(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Float.parseFloat(string(exchange, name)); } public static Double doubleValue(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Double.parseDouble(string(exchange, name)); } public static BigDecimal bigDecimalValue(final HttpServerExchange exchange, final String name) { return new BigDecimal(string(exchange, name)); } public static Long longValue(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Long.parseLong(string(exchange, name)); } public static Instant instant(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Instant.parse(string(exchange, name)); } public static Integer integerValue(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Integer.parseInt(string(exchange, name)); } public static Short shortValue(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Short.parseShort(string(exchange, name)); } public static Boolean booleanValue(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { return Boolean.parseBoolean(string(exchange, name)); } public static <T> T model(final HttpServerExchange exchange, final TypeReference<T> type) throws IllegalArgumentException, IOException { if (ServerPredicates.XML_PREDICATE.resolve(exchange)) { return xmlModel(exchange, type); } else { return jsonModel(exchange, type); } } public static <T> T model(final HttpServerExchange exchange, final Class<T> type) throws IllegalArgumentException, IOException { if (ServerPredicates.XML_PREDICATE.resolve(exchange)) { return xmlModel(exchange, type); } else { return jsonModel(exchange, type); } } public static Function<Method, HttpString> httpMethodFromMethod = (m) -> Arrays.stream(m.getDeclaredAnnotations()).map(a -> { if (a instanceof javax.ws.rs.POST) { return Methods.POST; } else if (a instanceof javax.ws.rs.GET) { return Methods.GET; } else if (a instanceof javax.ws.rs.PUT) { return Methods.PUT; } else if (a instanceof javax.ws.rs.DELETE) { return Methods.DELETE; } else if (a instanceof javax.ws.rs.OPTIONS) { return Methods.OPTIONS; } else if (a instanceof javax.ws.rs.HEAD) { return Methods.HEAD; } else { return null; } }).filter(Objects::nonNull).findFirst().get(); public static Function<Method, String> pathTemplateFromMethod = (m) -> { javax.ws.rs.Path childPath = m.getDeclaredAnnotation(javax.ws.rs.Path.class); javax.ws.rs.Path parentPath = m.getDeclaringClass().getDeclaredAnnotation(javax.ws.rs.Path.class); if (!childPath.value().equals("/")) { return (parentPath.value() + '/' + childPath.value()).replaceAll("\\/\\/", "\\/"); } return (parentPath.value()); }; }