PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/applinks-plugin/src/main/java/com/atlassian/applinks/core/auth/ApplicationLinkAnalyticsRequest.java

https://bitbucket.org/atlassian/application-links
Java | 291 lines | 234 code | 41 blank | 16 comment | 3 complexity | 32c1503d982f0b3d7789950fb9cf51fa MD5 | raw file
  1. package com.atlassian.applinks.core.auth;
  2. import com.atlassian.applinks.analytics.ApplinksRequestExecutionEvent;
  3. import com.atlassian.applinks.api.ApplicationLink;
  4. import com.atlassian.applinks.api.ApplicationLinkRequest;
  5. import com.atlassian.applinks.api.ApplicationLinkResponseHandler;
  6. import com.atlassian.event.api.EventPublisher;
  7. import com.atlassian.sal.api.net.RequestFilePart;
  8. import com.atlassian.sal.api.net.Response;
  9. import com.atlassian.sal.api.net.ResponseException;
  10. import com.atlassian.sal.api.net.ResponseHandler;
  11. import com.atlassian.sal.api.net.ReturningResponseHandler;
  12. import java.util.List;
  13. import java.util.Map;
  14. import java.util.Optional;
  15. import static java.util.Arrays.stream;
  16. import static java.util.Objects.requireNonNull;
  17. /**
  18. * Wrapper for {@link ApplicationLinkRequest} to send analytics for each request.
  19. *
  20. * @since 5.4.24
  21. */
  22. public class ApplicationLinkAnalyticsRequest implements ApplicationLinkRequest {
  23. private final ApplicationLink remoteApplink;
  24. private final EventPublisher publisher;
  25. private ApplicationLinkRequest wrappedRequest;
  26. private boolean unknownReqBodySize;
  27. private long reqBodySize;
  28. private int reqUrlSize;
  29. /**
  30. * Wraps a request to send analytics for each request.
  31. *
  32. * @param wrappedRequest Request to be wrapped.
  33. * @param remoteApplink The remoteApplink to the remote server.
  34. * @param publisher Event publisher for analytics.
  35. */
  36. ApplicationLinkAnalyticsRequest(final ApplicationLinkRequest wrappedRequest,
  37. final ApplicationLink remoteApplink,
  38. final EventPublisher publisher) {
  39. this.wrappedRequest = wrappedRequest;
  40. this.remoteApplink = requireNonNull(remoteApplink);
  41. this.publisher = requireNonNull(publisher);
  42. unknownReqBodySize = false;
  43. reqBodySize = 0;
  44. reqUrlSize = 0;
  45. }
  46. @Override
  47. public <R> R execute(final ApplicationLinkResponseHandler<R> responseHandler) throws ResponseException {
  48. ApplicationLinkAnalyticsResponseHandler<R> responseWrapper = new ApplicationLinkAnalyticsResponseHandler<>(responseHandler);
  49. R r = wrappedRequest.execute(responseWrapper);
  50. sendExecuteAnalytics(responseWrapper.getResponse()
  51. .map(ApplicationLinkAnalyticsRequest::getResponseSize)
  52. .orElse(null));
  53. return r;
  54. }
  55. @Override
  56. public ApplicationLinkRequest setConnectionTimeout(final int connectionTimeout) {
  57. wrappedRequest = wrappedRequest.setConnectionTimeout(connectionTimeout);
  58. return this;
  59. }
  60. @Override
  61. public ApplicationLinkRequest setSoTimeout(final int soTimeout) {
  62. wrappedRequest = wrappedRequest.setSoTimeout(soTimeout);
  63. return this;
  64. }
  65. @Override
  66. public ApplicationLinkRequest setUrl(final String url) {
  67. wrappedRequest = wrappedRequest.setUrl(url);
  68. this.reqUrlSize = url.getBytes().length;
  69. return this;
  70. }
  71. @Override
  72. public ApplicationLinkRequest setRequestBody(final String requestBody) {
  73. wrappedRequest = wrappedRequest.setRequestBody(requestBody);
  74. this.reqBodySize = requestBody.getBytes().length;
  75. this.unknownReqBodySize = false;
  76. return this;
  77. }
  78. @Override
  79. public ApplicationLinkRequest setRequestBody(final String requestBody, String contentType) {
  80. wrappedRequest = wrappedRequest.setRequestBody(requestBody, contentType);
  81. this.reqBodySize = requestBody.getBytes().length;
  82. this.unknownReqBodySize = false;
  83. return this;
  84. }
  85. @Override
  86. public ApplicationLinkRequest setFiles(final List<RequestFilePart> files) {
  87. wrappedRequest = wrappedRequest.setFiles(files);
  88. this.reqBodySize = files.stream()
  89. .map(f -> f.getFile().length())
  90. .mapToLong(Long::longValue)
  91. .sum();
  92. this.unknownReqBodySize = false;
  93. return this;
  94. }
  95. @Override
  96. public ApplicationLinkRequest setEntity(final Object entity) {
  97. wrappedRequest = wrappedRequest.setEntity(entity);
  98. // It's better not to return the size.
  99. this.reqBodySize = 0;
  100. this.unknownReqBodySize = true;
  101. return this;
  102. }
  103. @Override
  104. public ApplicationLinkRequest addRequestParameters(final String... params) {
  105. wrappedRequest = wrappedRequest.addRequestParameters(params);
  106. this.reqBodySize = stream(params)
  107. .map(p -> (long) p.getBytes().length)
  108. .mapToLong(Long::longValue)
  109. .sum();
  110. this.unknownReqBodySize = false;
  111. return this;
  112. }
  113. @Override
  114. public ApplicationLinkRequest addBasicAuthentication(final String hostname,
  115. final String username,
  116. final String password) {
  117. wrappedRequest = wrappedRequest.addBasicAuthentication(hostname, username, password);
  118. return this;
  119. }
  120. @Override
  121. public ApplicationLinkRequest addHeader(final String name, final String value) {
  122. wrappedRequest = wrappedRequest.addHeader(name, value);
  123. return this;
  124. }
  125. @Override
  126. public ApplicationLinkRequest setHeader(final String name, final String value) {
  127. wrappedRequest = wrappedRequest.setHeader(name, value);
  128. return this;
  129. }
  130. @Override
  131. public ApplicationLinkRequest setFollowRedirects(final boolean followRedirects) {
  132. wrappedRequest = wrappedRequest.setFollowRedirects(followRedirects);
  133. return this;
  134. }
  135. @Override
  136. public Map<String, List<String>> getHeaders() {
  137. return wrappedRequest.getHeaders();
  138. }
  139. @Override
  140. public void execute(final ResponseHandler<? super Response> responseHandler) throws ResponseException {
  141. AnalyticsResponseHandler<? super Response> responseWrapper
  142. = new AnalyticsResponseHandler<>(responseHandler);
  143. wrappedRequest.execute(responseWrapper);
  144. sendExecuteAnalytics(responseWrapper.getResponse()
  145. .map(ApplicationLinkAnalyticsRequest::getResponseSize)
  146. .orElse(null));
  147. }
  148. @Override
  149. public String execute() throws ResponseException {
  150. String result = wrappedRequest.execute();
  151. sendExecuteAnalytics(result != null ? (long) result.getBytes().length : null);
  152. return result;
  153. }
  154. @Override
  155. public <T> T executeAndReturn(final ReturningResponseHandler<? super Response, T> returningResponseHandler) throws ResponseException {
  156. ReturningAnalyticsResponseHandler<? super Response, T> responseWrapper
  157. = new ReturningAnalyticsResponseHandler<>(returningResponseHandler);
  158. T t = wrappedRequest.executeAndReturn(responseWrapper);
  159. sendExecuteAnalytics(responseWrapper.getResponse()
  160. .map(ApplicationLinkAnalyticsRequest::getResponseSize)
  161. .orElse(null));
  162. return t;
  163. }
  164. private void sendExecuteAnalytics(final Long approxResponseSize) {
  165. Long approxRequestSize;
  166. // If body size is unknown, send null, as payload size can't be estimated.
  167. if (unknownReqBodySize) {
  168. approxRequestSize = null;
  169. } else {
  170. approxRequestSize = reqBodySize + reqUrlSize + getHeaderSize();
  171. }
  172. publisher.publish(new ApplinksRequestExecutionEvent(approxRequestSize, approxResponseSize, remoteApplink.getId().get()));
  173. }
  174. private long getHeaderSize() {
  175. // Gets the size in bytes of the header by summing the length in bytes of
  176. // its various entries.
  177. return getHeaders().entrySet().stream()
  178. .map(entry -> {
  179. long keyLength = entry.getKey().getBytes().length;
  180. long valueLength = entry.getValue().stream()
  181. .map(v -> (long) v.getBytes().length)
  182. .mapToLong(Long::longValue)
  183. .sum();
  184. return keyLength + valueLength;
  185. }).mapToLong(Long::longValue).sum();
  186. }
  187. private static long getResponseSize(Response response) {
  188. long headerSize = response.getHeaders().entrySet().stream()
  189. .map(entry -> (long) entry.getKey().getBytes().length + entry.getValue().getBytes().length)
  190. .mapToLong(Long::longValue)
  191. .sum();
  192. long bodySize;
  193. try {
  194. bodySize = response.getResponseBodyAsString().getBytes().length;
  195. } catch (ResponseException e) {
  196. bodySize = 0;
  197. }
  198. return headerSize + bodySize;
  199. }
  200. protected static class ApplicationLinkAnalyticsResponseHandler<R> implements ApplicationLinkResponseHandler<R> {
  201. private final ApplicationLinkResponseHandler<R> wrappedResponseHandler;
  202. private Response response;
  203. ApplicationLinkAnalyticsResponseHandler(ApplicationLinkResponseHandler<R> wrappedResponseHandler) {
  204. this.wrappedResponseHandler = requireNonNull(wrappedResponseHandler);
  205. this.response = null;
  206. }
  207. public R credentialsRequired(Response response) throws ResponseException {
  208. return wrappedResponseHandler.credentialsRequired(response);
  209. }
  210. public R handle(Response response) throws ResponseException {
  211. this.response = response;
  212. return wrappedResponseHandler.handle(response);
  213. }
  214. Optional<Response> getResponse() {
  215. return Optional.ofNullable(response);
  216. }
  217. }
  218. protected static class AnalyticsResponseHandler<R extends Response> implements ResponseHandler<R> {
  219. private final ResponseHandler<R> wrappedResponseHandler;
  220. private R response;
  221. AnalyticsResponseHandler(ResponseHandler<R> wrappedResponseHandler) {
  222. this.wrappedResponseHandler = requireNonNull(wrappedResponseHandler);
  223. this.response = null;
  224. }
  225. @Override
  226. public void handle(R response) throws ResponseException {
  227. this.response = response;
  228. wrappedResponseHandler.handle(response);
  229. }
  230. Optional<Response> getResponse() {
  231. return Optional.ofNullable(response);
  232. }
  233. }
  234. protected static class ReturningAnalyticsResponseHandler<R extends Response, T> implements ReturningResponseHandler<R, T> {
  235. private final ReturningResponseHandler<R, T> wrappedResponseHandler;
  236. private R response;
  237. ReturningAnalyticsResponseHandler(ReturningResponseHandler<R, T> wrappedResponseHandler) {
  238. this.wrappedResponseHandler = requireNonNull(wrappedResponseHandler);
  239. this.response = null;
  240. }
  241. @Override
  242. public T handle(R response) throws ResponseException {
  243. this.response = response;
  244. return wrappedResponseHandler.handle(response);
  245. }
  246. Optional<Response> getResponse() {
  247. return Optional.ofNullable(response);
  248. }
  249. }
  250. }