001package com.plivo.api; 002 003import com.fasterxml.jackson.core.JsonGenerator; 004import com.fasterxml.jackson.core.JsonParser; 005import com.fasterxml.jackson.core.JsonProcessingException; 006import com.fasterxml.jackson.databind.BeanDescription; 007import com.fasterxml.jackson.databind.DeserializationConfig; 008import com.fasterxml.jackson.databind.DeserializationContext; 009import com.fasterxml.jackson.databind.DeserializationFeature; 010import com.fasterxml.jackson.databind.JavaType; 011import com.fasterxml.jackson.databind.JsonDeserializer; 012import com.fasterxml.jackson.databind.ObjectMapper; 013import com.fasterxml.jackson.databind.PropertyNamingStrategy; 014import com.fasterxml.jackson.databind.SerializationFeature; 015import com.fasterxml.jackson.databind.SerializerProvider; 016import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; 017import com.fasterxml.jackson.databind.module.SimpleModule; 018import com.fasterxml.jackson.databind.ser.std.StdSerializer; 019import com.plivo.api.models.base.LogLevel; 020import com.plivo.api.util.Utils; 021import okhttp3.Credentials; 022import okhttp3.Interceptor; 023import okhttp3.OkHttpClient; 024import okhttp3.Protocol; 025import okhttp3.Response; 026import okhttp3.ResponseBody; 027import okhttp3.logging.HttpLoggingInterceptor; 028import okhttp3.logging.HttpLoggingInterceptor.Level; 029import okhttp3.ConnectionPool; 030import retrofit2.Retrofit; 031import retrofit2.converter.jackson.JacksonConverterFactory; 032 033import java.io.BufferedReader; 034import java.io.IOException; 035import java.io.InputStream; 036import java.io.InputStreamReader; 037import java.net.ProtocolException; 038import java.text.SimpleDateFormat; 039import java.util.concurrent.TimeUnit; 040 041public class PlivoClient { 042 043 private static SimpleModule simpleModule = new SimpleModule(); 044 protected static String BASE_URL = "https://api.plivo.com/v1/"; 045 protected static String VOICE_BASE_URL = "https://api.plivo.com/v1/"; 046 protected static String VOICE_FALLBACK1_URL = "https://api.plivo.com/v1/"; 047 protected static String VOICE_FALLBACK2_URL = "https://api.plivo.com/v1/"; 048 protected static String CALLINSIGHTS_BASE_URL = "https://stats.plivo.com/v1/"; 049 protected static String LOOKUP_BASE_URL = "https://lookup.plivo.com/v1/"; 050 private static String version = "Unknown Version"; 051 private boolean testing = false; 052 private static ObjectMapper objectMapper = new ObjectMapper(); 053 054 public void setTesting(boolean testing) { 055 this.testing = testing; 056 } 057 058 public boolean isTesting() { 059 return testing; 060 } 061 062 static { 063 simpleModule.setDeserializerModifier(new BeanDeserializerModifier() { 064 @Override 065 public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, JavaType type, 066 BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 067 return new JsonDeserializer<Enum>() { 068 @Override 069 public Enum deserialize(JsonParser jp, DeserializationContext ctxt) 070 throws IOException, JsonProcessingException { 071 Class<? extends Enum> rawClass = (Class<Enum<?>>) type.getRawClass(); 072 return Enum.valueOf(rawClass, jp.getValueAsString().toUpperCase().replace("-", "_")); 073 } 074 }; 075 } 076 }); 077 simpleModule.addSerializer(Enum.class, new StdSerializer<Enum>(Enum.class) { 078 @Override 079 public void serialize(Enum value, JsonGenerator gen, SerializerProvider provider) 080 throws IOException { 081 gen.writeString(value.name().toLowerCase().replace("_", "-")); 082 } 083 }); 084 } 085 086 { 087 try { 088 InputStream inputStream = PlivoClient.class 089 .getResource("version.txt") 090 .openStream(); 091 092 version = new BufferedReader(new InputStreamReader(inputStream)).readLine(); 093 } catch (IOException ignored) { 094 ignored.printStackTrace(); 095 } 096 objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 097 objectMapper.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS); 098 objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); 099 objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 100 } 101 102 private Interceptor interceptor; 103 private final String authId; 104 private final String authToken; 105 private OkHttpClient httpClient; 106 private Retrofit retrofit; 107 private Retrofit voiceRetrofit; 108 private Retrofit voiceFallback1Retrofit; 109 private Retrofit voiceFallback2Retrofit; 110 private Retrofit callInsightsRetrofit; 111 private Retrofit lookupRetrofit; 112 private PlivoAPIService apiService = null; 113 private PlivoAPIService voiceApiService = null; 114 private PlivoAPIService voiceFallback1Service = null; 115 private PlivoAPIService voiceFallback2Service = null; 116 private CallInsightsAPIService callInsightsAPIService = null; 117 private LookupAPIService lookupAPIService = null; 118 119 /** 120 * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set 121 * the timeout and proxy using: 122 * 123 * <pre><code> 124 * new OkHttpClient.Builder() 125 * .proxy(proxy) 126 * .connectTimeout(1, TimeUnit.MINUTES); 127 * </code></pre> 128 * 129 * @param authId 130 * @param authToken 131 * @param httpClientBuilder 132 * @param baseUrl 133 * @param simpleModule 134 * @param logLevel 135 */ 136 public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder, final String baseUrl, final SimpleModule simpleModule, final LogLevel logLevel) { 137 if (!(Utils.isAccountIdValid(authId) || Utils.isSubaccountIdValid(authId))) { 138 throw new IllegalArgumentException("invalid account ID"); 139 } 140 141 this.authId = authId; 142 this.authToken = authToken; 143 this.objectMapper.registerModule(simpleModule); 144 this.interceptor = new HttpLoggingInterceptor().setLevel(Level.valueOf(logLevel.toString())); 145 146 httpClient = httpClientBuilder 147 .addNetworkInterceptor(interceptor) 148 .addInterceptor(chain -> chain.proceed( 149 chain.request() 150 .newBuilder() 151 .addHeader("Authorization", Credentials.basic(getAuthId(), getAuthToken())) 152 .addHeader("User-Agent", String.format("%s/%s (Implementation: %s %s %s, Specification: %s %s %s)", "plivo-java", version, 153 Runtime.class.getPackage().getImplementationVendor(), 154 Runtime.class.getPackage().getImplementationTitle(), 155 Runtime.class.getPackage().getImplementationVersion(), 156 Runtime.class.getPackage().getSpecificationVendor(), 157 Runtime.class.getPackage().getSpecificationTitle(), 158 Runtime.class.getPackage().getSpecificationVersion() 159 )) 160 .build() 161 )) 162 .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)) 163 .addNetworkInterceptor(chain -> { 164 Response response; 165 try { 166 response = chain.proceed(chain.request()); 167 } catch (ProtocolException protocolException) { 168 // We return bodies for HTTP 204! 169 response = new Response.Builder() 170 .request(chain.request()) 171 .code(204) 172 .protocol(Protocol.HTTP_1_1) 173 .body(ResponseBody.create(null, new byte[]{})) 174 .build(); 175 } 176 return response; 177 }).build(); 178 179 retrofit = new Retrofit.Builder() 180 .client(httpClient) 181 .baseUrl(baseUrl) 182 .addConverterFactory(JacksonConverterFactory.create(objectMapper)) 183 .build(); 184 185 this.apiService = retrofit.create(PlivoAPIService.class); 186 187 voiceRetrofit = new Retrofit.Builder() 188 .client(httpClient) 189 .baseUrl((VOICE_BASE_URL)) 190 .addConverterFactory(JacksonConverterFactory.create(objectMapper)) 191 .build(); 192 193 this.voiceApiService = voiceRetrofit.create(PlivoAPIService.class); 194 195 voiceFallback1Retrofit = new Retrofit.Builder() 196 .client(httpClient) 197 .baseUrl((VOICE_FALLBACK1_URL)) 198 .addConverterFactory(JacksonConverterFactory.create(objectMapper)) 199 .build(); 200 201 this.voiceFallback1Service = voiceFallback1Retrofit.create(PlivoAPIService.class); 202 203 voiceFallback2Retrofit = new Retrofit.Builder() 204 .client(httpClient) 205 .baseUrl((VOICE_FALLBACK2_URL)) 206 .addConverterFactory(JacksonConverterFactory.create(objectMapper)) 207 .build(); 208 209 this.voiceFallback2Service = voiceFallback2Retrofit.create(PlivoAPIService.class); 210 211 callInsightsRetrofit = new Retrofit.Builder() 212 .client(httpClient) 213 .baseUrl((CALLINSIGHTS_BASE_URL)) 214 .addConverterFactory(JacksonConverterFactory.create(objectMapper)) 215 .build(); 216 217 this.callInsightsAPIService = callInsightsRetrofit.create(CallInsightsAPIService.class); 218 219 lookupRetrofit = new Retrofit.Builder() 220 .client(httpClient) 221 .baseUrl((LOOKUP_BASE_URL)) 222 .addConverterFactory(JacksonConverterFactory.create(objectMapper)) 223 .build(); 224 225 this.lookupAPIService = lookupRetrofit.create(LookupAPIService.class); 226 } 227 228 /** 229 * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set 230 * the timeout and proxy using: 231 * 232 * <pre><code> 233 * new OkHttpClient.Builder() 234 * .proxy(proxy) 235 * .connectTimeout(1, TimeUnit.MINUTES); 236 * </code></pre> 237 * 238 * @param authId 239 * @param authToken 240 */ 241 public PlivoClient(String authId, String authToken) { 242 this(authId, authToken, new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS), BASE_URL, simpleModule, LogLevel.NONE); 243 } 244 245 /** 246 * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set 247 * the timeout and proxy using: 248 * 249 * <pre><code> 250 * new OkHttpClient.Builder() 251 * .proxy(proxy) 252 * .connectTimeout(1, TimeUnit.MINUTES); 253 * </code></pre> 254 * 255 * @param authId 256 * @param authToken 257 * @param httpClientBuilder 258 */ 259 public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder) { 260 this(authId, authToken, httpClientBuilder, BASE_URL, simpleModule, LogLevel.NONE); 261 } 262 263 /** 264 * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set 265 * the timeout and proxy using: 266 * 267 * <pre><code> 268 * new OkHttpClient.Builder() 269 * .proxy(proxy) 270 * .connectTimeout(1, TimeUnit.MINUTES); 271 * </code></pre> 272 * 273 * To set Log level you can pass LogLevel enum.It can be set to following values: 274 * 275 * NONE - No logs 276 * BASIC - Log request and response line 277 * HEADER - Log request and response line along with their headers 278 * BODY - Log request and response line along with their headers and bodies 279 * 280 * @param authId 281 * @param authToken 282 * @param logLevel 283 */ 284 public PlivoClient(String authId, String authToken, LogLevel logLevel) { 285 this(authId, authToken, new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS), BASE_URL, simpleModule, logLevel); 286 } 287 288 /** 289 * Constructs a new PlivoClient instance. To set a proxy, timeout etc, you can pass in an OkHttpClient.Builder, on which you can set 290 * the timeout and proxy using: 291 * 292 * <pre><code> 293 * new OkHttpClient.Builder() 294 * .proxy(proxy) 295 * .connectTimeout(1, TimeUnit.MINUTES); 296 * </code></pre> 297 * To set Log level you can pass LogLevel enum.It can be set to following values: 298 * 299 * NONE - No logs 300 * BASIC - Log request and response line 301 * HEADER - Log request and response line along with their headers 302 * BODY - Log request and response line along with their headers and bodies 303 * 304 * @param authId 305 * @param authToken 306 * @param httpClientBuilder 307 * @param logLevel 308 */ 309 public PlivoClient(String authId, String authToken, OkHttpClient.Builder httpClientBuilder, LogLevel logLevel) { 310 this(authId, authToken, httpClientBuilder, BASE_URL, simpleModule, logLevel); 311 } 312 313 public static ObjectMapper getObjectMapper() { 314 return objectMapper; 315 } 316 317 Retrofit getRetrofit() { 318 return retrofit; 319 } 320 321 public PlivoAPIService getApiService() { 322 return apiService; 323 } 324 325 public PlivoAPIService getVoiceApiService(){ 326 return voiceApiService; 327 } 328 329 public PlivoAPIService getVoiceFallback1Service(){ 330 return voiceFallback1Service; 331 } 332 333 public PlivoAPIService getVoiceFallback2Service(){ 334 return voiceFallback2Service; 335 } 336 337 public CallInsightsAPIService getCallInsightsAPIService() { 338 return callInsightsAPIService; 339 } 340 341 public LookupAPIService getLookupAPIService() { 342 return lookupAPIService; 343 } 344 345 void setApiService(PlivoAPIService apiService) { 346 this.apiService = apiService; 347 } 348 349 public String getAuthId() { 350 return authId; 351 } 352 353 public String getAuthToken() { 354 return authToken; 355 } 356}