package io.evanwong.oss.hipchat.v2.commons; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import org.apache.commons.codec.EncoderException; import org.apache.commons.codec.net.URLCodec; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; public abstract class Request<T> { private static final Logger log = LoggerFactory.getLogger(Request.class); // protected static final String BASE_URL = "https://api.hipchat.com/v2"; protected String baseUrl = "https://api.hipchat.com/v2"; protected ExecutorService executorService; protected String accessToken; protected HttpClient httpClient; private final ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); protected final ObjectWriter objectWriter = objectMapper.writer(); protected final ObjectReader objectReader; protected abstract Map<String, Object> toQueryMap(); protected abstract HttpResponse request() throws IOException; protected abstract String getPath(); protected String getEncodedPath() { String path = getPath(); String[] tokens = path.split("/"); String encodedPath = ""; URLCodec urlCodec = new URLCodec(); try { for (String token : tokens) { if (!token.isEmpty()) { //replace + to %20 encodedPath += "/" + urlCodec.encode(token).replace("+", "%20"); } } } catch (EncoderException e) { log.error("Failed to encode the path properly.", e); } return encodedPath; } protected Request(String accessToken, String baseUrl, HttpClient httpClient, ExecutorService executorService) { this.executorService = executorService; this.accessToken = accessToken; this.httpClient = httpClient; this.baseUrl = baseUrl; this.objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); this.objectReader = objectMapper.reader(getParameterClass()); } public Future<T> execute() { Future<T> future = executorService.submit(() -> { HttpResponse response = request(); int status = response.getStatusLine().getStatusCode(); HttpEntity entity = response.getEntity(); String content = entity != null ? EntityUtils.toString(entity) : null; if (status >= 200 && status < 300) { if (content == null) { //should be NoContent return getParameterClass().newInstance(); } return objectReader.readValue(content); } else { log.error("Invalid response status: {}, content: {}", status, content); return null; } }); return future; } protected Class<T> getParameterClass() { return (Class<T>) (((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]); } }