// Path: src/main/java/si/mazi/rescu/serialization/PlainTextResponseReader.java
// public class PlainTextResponseReader extends ResponseReader {
//     private static final Logger log = LoggerFactory.getLogger(PlainTextResponseReader.class);
// 
//     public PlainTextResponseReader(boolean ignoreHttpErrorCodes) {
//         super(ignoreHttpErrorCodes);
//     }
// 
//     @SuppressWarnings("unchecked")
//     @Override
//     public String read(String httpBody, Type returnType) throws IOException {
//         return httpBody;
//     }
// 
//     @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
//     @Override
//     protected RuntimeException readException(String httpBody, Class<? extends RuntimeException> exceptionType) throws IOException {
//         final String message = read(httpBody, exceptionType);
//         RuntimeException constructedException = null;
// 
//         ReflectiveOperationException reflectiveOperationException = null;
//         try {
//             // try constructng the exception with message
//             constructedException = exceptionType.getConstructor(String.class).newInstance(message);
//         } catch (ReflectiveOperationException e) {
//             reflectiveOperationException = e;
//         }
// 
//         if (reflectiveOperationException != null) {
//             reflectiveOperationException = null;
// 
//             try {
//                 // fallback to no-parameter constructor
//                 constructedException = exceptionType.getDeclaredConstructor().newInstance();
//                 log.warn("Cannot construct a {} with message parameter. Ommiting the message, which was: {}", exceptionType, message);
//             } catch (ReflectiveOperationException e) {
//                 reflectiveOperationException = e;
//             }
// 
//             if (reflectiveOperationException != null) {
//                 log.warn("Cannot construct a {}. Throwing a RuntimeException instead. Main cause: {}", exceptionType, reflectiveOperationException.toString());
//                 throw new RuntimeException(message);
//             }
//         }
//         return constructedException;
//     }
// }
// 
// Path: src/main/java/si/mazi/rescu/serialization/jackson/DefaultJacksonObjectMapperFactory.java
// public class DefaultJacksonObjectMapperFactory implements JacksonObjectMapperFactory {
// 
//     /**
//      * Creates a configured instance of <code>ObjectMapper</code>.
//      *
//      * @return configured instance of <code>ObjectMapper</code>
//      */
//     @Override
//     public ObjectMapper createObjectMapper() {
//         ObjectMapper objectMapper = createInstance();
//         configureObjectMapper(objectMapper);
//         return objectMapper;
//     }
// 
//     @Override
//     public void configureObjectMapper(ObjectMapper objectMapper) {
//         objectMapper.setAnnotationIntrospector(new IgnoreThrowableProperties());
//         objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//         objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true);
//     }
// 
//     /**
//      * Allows subclasing and overriding of ObjectMapper instances
//      *
//      * @return instance of <code>ObjectMapper</code>
//      */
//     protected ObjectMapper createInstance() {
//         return new ObjectMapper();
//     }
// 
// }
// 
// Path: src/main/java/si/mazi/rescu/serialization/jackson/JacksonObjectMapperFactory.java
// public interface JacksonObjectMapperFactory extends JacksonConfigureListener {
// 
//     /**
//      * Creates a configured instance of <code>ObjectMapper</code>.
//      *
//      * @return configured instance of <code>ObjectMapper</code>
//      */
//     ObjectMapper createObjectMapper();
// 
//     /**
//      * Allow configuration after <code>ObjectMapper</code> creation. For example, the users
//      * might want to register modules with nonstandard (de)serializers now.
//      *
//      * @param objectMapper the ObjectMapper to configure.
//      */
//     void configureObjectMapper(ObjectMapper objectMapper);
// 
// }
// 
// Path: src/main/java/si/mazi/rescu/serialization/jackson/JacksonResponseReader.java
// public class JacksonResponseReader extends ResponseReader {
// 
//     private final ObjectMapper objectMapper;
// 
//     public JacksonResponseReader(ObjectMapper objectMapper, boolean ignoreHttpErrorCodes) {
//         super(ignoreHttpErrorCodes);
//         this.objectMapper = objectMapper;
//     }
// 
//     public <T> T read(String httpBody, Type returnType) throws IOException {
//         JavaType javaType = objectMapper
//                 .getTypeFactory()
//                 .constructType(returnType);
// 
//         return objectMapper.readValue(httpBody, javaType);
//     }
// 
//     @Override
//     protected RuntimeException readException(String httpBody, Class<? extends RuntimeException> exceptionType) throws IOException {
//         return read(httpBody, exceptionType);
//     }
// 
// }


import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import si.mazi.rescu.serialization.PlainTextResponseReader;
import si.mazi.rescu.serialization.jackson.DefaultJacksonObjectMapperFactory;
import si.mazi.rescu.serialization.jackson.JacksonObjectMapperFactory;
import si.mazi.rescu.serialization.jackson.JacksonResponseReader;
import javax.ws.rs.Path;
        HttpURLConnection connection = null;
        RestInvocation invocation = null;
        Object lock = getValueGenerator(args);
        if (lock == null) {
            lock = new Object(); // effectively no locking
        }
        try {
            synchronized (lock) {
                invocation = createInvocation(method, args);
                connection = invokeHttp(invocation);
            }
            final Object result = receiveAndMap(methodMetadata, connection);
            makeAware(result, connection, invocation);
            return result;
        } catch (Exception e) {
            final boolean madeAware = makeAware(e, connection, invocation);
            if (config.isWrapUnexpectedExceptions() && !madeAware) {
                throw new AwareException(e, invocation);
            }
            throw e;
        }
    }

    private boolean makeAware(Object result, HttpURLConnection connection, RestInvocation invocation) {
        boolean madeAware = false;
        if (result instanceof InvocationAware) {
            try {
                ((InvocationAware) result).setInvocation(invocation);
                madeAware = true;
            } catch (Exception ex) {
// Path: src/main/java/si/mazi/rescu/serialization/PlainTextResponseReader.java
// public class PlainTextResponseReader extends ResponseReader {
//     private static final Logger log = LoggerFactory.getLogger(PlainTextResponseReader.class);
// 
//     public PlainTextResponseReader(boolean ignoreHttpErrorCodes) {
//         super(ignoreHttpErrorCodes);
//     }
// 
//     @SuppressWarnings("unchecked")
//     @Override
//     public String read(String httpBody, Type returnType) throws IOException {
//         return httpBody;
//     }
// 
//     @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
//     @Override
//     protected RuntimeException readException(String httpBody, Class<? extends RuntimeException> exceptionType) throws IOException {
//         final String message = read(httpBody, exceptionType);
//         RuntimeException constructedException = null;
// 
//         ReflectiveOperationException reflectiveOperationException = null;
//         try {
//             // try constructng the exception with message
//             constructedException = exceptionType.getConstructor(String.class).newInstance(message);
//         } catch (ReflectiveOperationException e) {
//             reflectiveOperationException = e;
//         }
// 
//         if (reflectiveOperationException != null) {
//             reflectiveOperationException = null;
// 
//             try {
//                 // fallback to no-parameter constructor
//                 constructedException = exceptionType.getDeclaredConstructor().newInstance();
//                 log.warn("Cannot construct a {} with message parameter. Ommiting the message, which was: {}", exceptionType, message);
//             } catch (ReflectiveOperationException e) {
//                 reflectiveOperationException = e;
//             }
// 
//             if (reflectiveOperationException != null) {
//                 log.warn("Cannot construct a {}. Throwing a RuntimeException instead. Main cause: {}", exceptionType, reflectiveOperationException.toString());
//                 throw new RuntimeException(message);
//             }
//         }
//         return constructedException;
//     }
// }
// 
// Path: src/main/java/si/mazi/rescu/serialization/jackson/DefaultJacksonObjectMapperFactory.java
// public class DefaultJacksonObjectMapperFactory implements JacksonObjectMapperFactory {
// 
//     /**
//      * Creates a configured instance of <code>ObjectMapper</code>.
//      *
//      * @return configured instance of <code>ObjectMapper</code>
//      */
//     @Override
//     public ObjectMapper createObjectMapper() {
//         ObjectMapper objectMapper = createInstance();
//         configureObjectMapper(objectMapper);
//         return objectMapper;
//     }
// 
//     @Override
//     public void configureObjectMapper(ObjectMapper objectMapper) {
//         objectMapper.setAnnotationIntrospector(new IgnoreThrowableProperties());
//         objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//         objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true);
//     }
// 
//     /**
//      * Allows subclasing and overriding of ObjectMapper instances
//      *
//      * @return instance of <code>ObjectMapper</code>
//      */
//     protected ObjectMapper createInstance() {
//         return new ObjectMapper();
//     }
// 
// }
// 
// Path: src/main/java/si/mazi/rescu/serialization/jackson/JacksonObjectMapperFactory.java
// public interface JacksonObjectMapperFactory extends JacksonConfigureListener {
// 
//     /**
//      * Creates a configured instance of <code>ObjectMapper</code>.
//      *
//      * @return configured instance of <code>ObjectMapper</code>
//      */
//     ObjectMapper createObjectMapper();
// 
//     /**
//      * Allow configuration after <code>ObjectMapper</code> creation. For example, the users
//      * might want to register modules with nonstandard (de)serializers now.
//      *
//      * @param objectMapper the ObjectMapper to configure.
//      */
//     void configureObjectMapper(ObjectMapper objectMapper);
// 
// }
// 
// Path: src/main/java/si/mazi/rescu/serialization/jackson/JacksonResponseReader.java
// public class JacksonResponseReader extends ResponseReader {
// 
//     private final ObjectMapper objectMapper;
// 
//     public JacksonResponseReader(ObjectMapper objectMapper, boolean ignoreHttpErrorCodes) {
//         super(ignoreHttpErrorCodes);
//         this.objectMapper = objectMapper;
//     }
// 
//     public <T> T read(String httpBody, Type returnType) throws IOException {
//         JavaType javaType = objectMapper
//                 .getTypeFactory()
//                 .constructType(returnType);
// 
//         return objectMapper.readValue(httpBody, javaType);
//     }
// 
//     @Override
//     protected RuntimeException readException(String httpBody, Class<? extends RuntimeException> exceptionType) throws IOException {
//         return read(httpBody, exceptionType);
//     }
// 
// }


// Path: src/main/java/si/mazi/rescu/RestInvocationHandler.java
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import si.mazi.rescu.serialization.PlainTextResponseReader;
import si.mazi.rescu.serialization.jackson.DefaultJacksonObjectMapperFactory;
import si.mazi.rescu.serialization.jackson.JacksonObjectMapperFactory;
import si.mazi.rescu.serialization.jackson.JacksonResponseReader;
import javax.ws.rs.Path;

        HttpURLConnection connection = null;
        RestInvocation invocation = null;
        Object lock = getValueGenerator(args);
        if (lock == null) {
            lock = new Object(); // effectively no locking
        }
        try {
            synchronized (lock) {
                invocation = createInvocation(method, args);
                connection = invokeHttp(invocation);
            }
            final Object result = receiveAndMap(methodMetadata, connection);
            makeAware(result, connection, invocation);
            return result;
        } catch (Exception e) {
            final boolean madeAware = makeAware(e, connection, invocation);
            if (config.isWrapUnexpectedExceptions() && !madeAware) {
                throw new AwareException(e, invocation);
            }
            throw e;
        }
    }

    private boolean makeAware(Object result, HttpURLConnection connection, RestInvocation invocation) {
        boolean madeAware = false;
        if (result instanceof InvocationAware) {
            try {
                ((InvocationAware) result).setInvocation(invocation);
                madeAware = true;
            } catch (Exception ex) {
                log.warn("Failed to set invocation on the InvocationAware", ex);