├── .github └── dependabot.yml ├── .gitignore ├── .travis.yml ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── brsanthu │ └── googleanalytics │ ├── GoogleAnalytics.java │ ├── GoogleAnalyticsBuilder.java │ ├── GoogleAnalyticsConfig.java │ ├── GoogleAnalyticsException.java │ ├── GoogleAnalyticsExecutor.java │ ├── GoogleAnalyticsStats.java │ ├── discovery │ ├── AwtRequestParameterDiscoverer.java │ ├── DefaultRequestParameterDiscoverer.java │ └── RequestParameterDiscoverer.java │ ├── httpclient │ ├── ApacheHttpClientImpl.java │ ├── BatchUrlEncodedFormEntity.java │ ├── GoogleAnalyticsThreadFactory.java │ ├── HttpBatchRequest.java │ ├── HttpBatchResponse.java │ ├── HttpClient.java │ ├── HttpRequest.java │ ├── HttpResponse.java │ ├── NameValuePair.java │ └── OkHttpClientImpl.java │ ├── internal │ ├── Constants.java │ ├── GaUtils.java │ ├── GoogleAnalyticsImpl.java │ ├── GoogleAnalyticsStatsImpl.java │ └── ParameterGetterSetterGenerator.java │ ├── logger │ ├── DefaultLoggerFactory.java │ ├── Logger.java │ └── LoggerFactory.java │ └── request │ ├── DefaultRequest.java │ ├── EventHit.java │ ├── ExceptionHit.java │ ├── GoogleAnalyticsParameter.java │ ├── GoogleAnalyticsRequest.java │ ├── GoogleAnalyticsResponse.java │ ├── ItemHit.java │ ├── PageViewHit.java │ ├── ScreenViewHit.java │ ├── SocialHit.java │ ├── TimingHit.java │ └── TransactionHit.java └── test └── java └── com └── brsanthu └── googleanalytics ├── AwtRequestParameterDiscovererTest.java ├── DefaultRequestParameterDiscovererTest.java ├── EventHitTest.java ├── GaUtilsTest.java ├── GoogleAnalyticsApacheHttpTest.java ├── GoogleAnalyticsBatchTest.java ├── GoogleAnalyticsConfigTest.java ├── GoogleAnalyticsOkHttpTest.java ├── GoogleAnalyticsParameterTest.java ├── GoogleAnalyticsRequestTest.java ├── HitTypesTest.java └── SamplingTest.java /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | - package-ecosystem: maven 10 | directory: "/" 11 | schedule: 12 | interval: daily 13 | time: "10:00" 14 | open-pull-requests-limit: 10 15 | target-branch: java7 16 | ignore: 17 | - dependency-name: com.squareup.okhttp3:okhttp 18 | versions: 19 | - ">= 3.13.a, < 3.14" 20 | - dependency-name: com.squareup.okhttp3:okhttp 21 | versions: 22 | - ">= 3.14.a, < 3.15" 23 | - dependency-name: com.squareup.okhttp3:okhttp 24 | versions: 25 | - ">= 4.a, < 5" 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Maven publish settings 4 | settings.xml 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | META-INF/ 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | 16 | # Eclipse Core 17 | 18 | .classpath 19 | .project 20 | .settings/ 21 | .metadata/ 22 | .recommenders/ 23 | run-* 24 | 25 | # Ignore build artfacts at all sub folder level 26 | target/ 27 | bin/ 28 | src/test/resources/run-* 29 | 30 | # Locally stored "Eclipse launch configurations" 31 | *.launch 32 | 33 | # Eclipse CDT-specific 34 | .cproject 35 | 36 | ## IntelliJ 37 | *.iml 38 | .idea/ 39 | 40 | # Java annotation processor (APT) 41 | .factorypath 42 | 43 | # PDT-specific 44 | .buildpath 45 | 46 | # sbteclipse plugin 47 | .target 48 | 49 | # TeXlipse plugin 50 | .texlipse 51 | 52 | # STS (Spring Tool Suite) 53 | .springBeans 54 | 55 | #Netbeans 56 | nbactions.xml 57 | 58 | #Mac finder files 59 | .DS_Store 60 | 61 | #node js 62 | node_modules/ 63 | 64 | #JRebel files 65 | rebel.xml 66 | .rebel.xml.bak 67 | 68 | nohup.out 69 | 70 | core-search-service 71 | core-dns-service 72 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false 3 | 4 | jdk: 5 | - openjdk8 6 | 7 | env: 8 | - DISPLAY=:99.0 9 | 10 | services: 11 | - xvfb 12 | 13 | install: true 14 | 15 | script: 16 | - mvn package -Dgpg.skip=true 17 | 18 | before_cache: 19 | - rm -fr $HOME/.m2/repository/net/mikehardy/google-analytics-java 20 | 21 | cache: 22 | directories: 23 | - $HOME/.m2 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Overview 2 | == 3 | Java API for [Google Analytics Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/v1/), with the Open Source compatible Apache 2.0 license 4 | 5 | The library is available from Maven Central. Add the following dependency, replacing `$google-analytics-version` with the current stable version number: 6 | 7 | Maven: 8 | 9 | 10 | net.mikehardy 11 | google-analytics-java 12 | $google-analytics-version 13 | 14 | 15 | Gradle: 16 | 17 | implementation 'net.mikehardy:google-analytics-java:$google-analytics-version' 18 | 19 | Others: [Check Here](https://search.maven.org/#artifactdetails%7Cnet.mikehardy) 20 | 21 | To get a local build, do 22 | 23 | git clone https://github.com/mikehardy/google-analytics-java.git 24 | mvn install 25 | 26 | View Javadocs [here](https://www.javadoc.io/doc/net.mikehardy/google-analytics-java) 27 | 28 | The fluent API is very easy to use, with sensible default options. 29 | 30 | The library uses Java 1.8 features only available in Android7/API24/Nougat or higher, but there is a supported version compatible with Java 1.7 (and Android down to at least API 15) using the [fairly amazing Java streamsupport / completable-futures compatibility library](https://github.com/stefan-zobel/streamsupport). If you need that, you'll just want to alter the dependency to use the "google-analytics-java7" artifact instead of "google-analytics-java" 31 | 32 | 33 | Features 34 | == 35 | This library implements the measurement protocol with following features. 36 | 37 | * Supports all parameters and hit types. 38 | * Able to configure default parameters, which would be used for each request. 39 | * Type-safe data types as appropriate (String, Integer, Double and Boolean) 40 | * Convenient hit specific request types for easy construction. 41 | * Synchronous or Asynchronous Event Processing. 42 | * Support for delayed request construction. 43 | * Asynchronous processing uses Java Concurrent Executor Service. 44 | * Uses the latest Apache Http or OkHttp client for high performing event posting. 45 | * Event posting can be enabled/disabled at runtime at configuration level. 46 | * Supports connections via Proxy 47 | * Gathers some basic information from the underlying Jvm (File Encoding, User Language, Screen Size, Color Depth etc) 48 | * Validates the request and can throw exception or log warning if validation fails (still wip) 49 | * Logging uses SLF4J api 50 | * Gathers basic stats (number of events posted for each hit type) if requested in the configuration. 51 | * Implementation is Thread Safe 52 | * Jar files are OSGi ready, so could be used with Eclipse 53 | * Build against Java 1.8 54 | * Supports batching of requests 55 | * Complete Measurement Protocol parameter information is made available as Javadocs 56 | 57 | Usage 58 | == 59 | 60 | Init 61 | -- 62 | Before using the library to post events, `GoogleAnalytics` instance needs to be initialized. Once it is initialized, same instance can be used 63 | to post events across multiple threads and instance is designed to be thread-safe. 64 | 65 | It can be initialized with two types of information. Set of information called configuration (via `GoogleAnalyticsConfig`), which is used by the library and default request settings (`DefaultRequest`), which defines the default attributes for all subsequent requests. 66 | 67 | Builder also provides typed methods to set most-relevant attributes of default request for readability. 68 | 69 | Simplified initialization with all defaults is as follows. 70 | 71 | ga = GoogleAnalytics.builder() 72 | .withTrackingId("UA-00000000") 73 | .build(); 74 | 75 | To build with custom configuration: 76 | 77 | ga = GoogleAnalytics.builder() 78 | .withConfig(new GoogleAnalyticsConfig().setBatchingEnabled(true).setBatchSize(10)) 79 | .withTrackingId("UA-00000000") 80 | .build(); 81 | 82 | To build with custom configuration and some default request attributes: 83 | 84 | ga = GoogleAnalytics.builder() 85 | .withConfig(new GoogleAnalyticsConfig().setBatchingEnabled(true).setBatchSize(10)) 86 | .withDefaultRequest(new DefaultRequest().userIp("127.0.0.1").trackingId("UA-00000000")) 87 | .build(); 88 | 89 | Note that tracking id can be set to one value for all requests (using default request attributes) or it can be set on per request basis. 90 | 91 | Sending Events 92 | -- 93 | To send requests, create one of the event type requests, configure the values for that event and call `send()`. 94 | 95 | Here are some examples: 96 | 97 | ga.screenView() 98 | .sessionControl("start") 99 | .send(); 100 | 101 | ga.pageView() 102 | .documentTitle(entry.getPage()) 103 | .documentPath("/" + entry.getPage()) 104 | .clientId("Some Id") 105 | .customDimension(1, "Product") 106 | .customDimension(1, "Version") 107 | .userIp("198.165.0.1") 108 | .send(); 109 | 110 | ga.exception() 111 | .exceptionDescription(e.getMessage()) 112 | .send(); 113 | 114 | ga.screenView() 115 | .sessionControl("end") 116 | .send(); 117 | 118 | Async Posting 119 | -- 120 | Sending request to Google Analytics is network call and hence it may take a little bit of time. If you would like to avoid this overhead, you can opt in 121 | to send requests asynchronously. 122 | 123 | Executor is created to process the requests async with default config of `minThreads=0, maxThreads=5, threadFormat=googleanalyticsjava-thread-{0}, threadTimeoutSecs=300, queueSize=1000. rejectExecutor=CallerRunsPolicy`. 124 | 125 | If you want to change these values, configure them before building `GoogleAnalytics` instance. You can also set your own executor in the config, in that case that executor will be used. 126 | 127 | To send request async, call `.sendAsync()` instead of `.send()` as follows 128 | 129 | ga.screenView() 130 | .sessionControl("end") 131 | .sendAsync(); 132 | 133 | Batching 134 | -- 135 | Google Analytics api supports sending events in batch to reduce the network overhead. Batching is disabled by default but it can be enabled using `batchingEnabled` config. This needs to be set before Google Analytics is built. 136 | 137 | Once batching is enabled, usage is same as non-batching. Upon submission, request will be held in a internal list and upon reaching the batch limit, it will be posted to Google api. Note that batching can be used along with Async posting and it work in the same way. 138 | 139 | Max batch size is 20 requests and that is the default, which can be changed using config `batchSize` 140 | 141 | Master Switch 142 | -- 143 | Library provides a master switch with config `enabled`. If set to `false` then requests will be accepted and silently dropped. This config variable can be changed before or after building the `ga` instance. 144 | 145 | Discovering Request Parameters 146 | -- 147 | Library tries to discover some default request parameters, which is controlled via config `discoverRequestParameters` with default value of `true`. Parameters are discovered during the building process so it is one time activity. 148 | 149 | It discovers following parameters: 150 | 151 | * user agent 152 | * user language 153 | * document encoding 154 | * screen resolution 155 | * screen colors 156 | 157 | To discover screen resolution and colors, it needs access to `java.awt`. Since not all environments have access to awt, it is not enabled by default. If would like to use it, set config `requestParameterDiscoverer` to instance of `AwtRequestParameterDiscoverer` 158 | 159 | Http Client 160 | -- 161 | Library abstracts http client interaction via `HttpClient` interface with default implementation based on Apache HttpClient. If you want to use your own version of http client, set config `httpClient`. 162 | 163 | 164 | Release Notes 165 | == 166 | Version 2.0.11 - Mar 3 2020 167 | -- 168 | * Dependency updates 169 | 170 | Version 2.0.10 - Jan 6 2019 171 | -- 172 | * fixed ConcurrentModificationException in ApacheClient batch post 173 | * dependency updates 174 | * logging during test 175 | 176 | Version 2.0.9 - Dec 19 2019 177 | -- 178 | * dependency updates 179 | * update CI 180 | 181 | Version 2.0.8 - Jun 27 2019 182 | -- 183 | * Various dependency updates, notably OkHTTP 3 -> 4 184 | 185 | Version 2.0.7 - Feb 19 2019 186 | -- 187 | * Various dependency updates 188 | * Fix APIs to return CompletableFuture instead of Future (thanks @jtjeferreira!) 189 | 190 | Version 2.0.6 - Dec 12 2018 191 | -- 192 | * Compatibility - An evolution of 2.0.5, but "Java8-clean", Java7 compatibility split into separate artifact 193 | 194 | Version 2.0.5 - Dec 12 2018 195 | -- 196 | * Enhancement - HTTP calls are Java-async now, still Android/Java7 compatible (thanks @jtjeferreira!) 197 | 198 | Version 2.0.4 - Oct 14 2018 199 | -- 200 | * Compatibility - removed slf4j-simple from test to prevent dependency pollution (#20) 201 | * Enhancement - added more API abilities to manipulate sampling, and added sampling test 202 | 203 | Version 2.0.3 - Oct 12 2018 204 | -- 205 | * Compatibility - Altered Core and OkHttpClientImpl so it worked with minSDK / API15 on Android 206 | 207 | Version 2.0.2 - Oct 12 2018 208 | -- 209 | * Error - Fixed #11 - not closing OkHttp response body in postBatch() 210 | * Enhancement - Fixed #16 - implemented basic sampling strategy with GoogleAnalyticsConfig.setSamplePercentage(int) 211 | * Enhancement - request parameters are alphabetically ordered so they are predictable now 212 | * Build - fix javadoc generation on JDK10+ 213 | 214 | Version 2.0.1 - Oct 02 2018 215 | -- 216 | * Implement OkHttp transport as an option 217 | 218 | Version 2.0.0 - Jan 24 2018 219 | -- 220 | * API redesign based on builder and fluent pattern 221 | * Added support for batching requests 222 | 223 | Version 1.1.2 - Apr 29 2015 224 | -- 225 | 226 | 227 | Version 1.1.1 - May 21 2014 228 | -- 229 | * Fixed the issue #14. Https Collection url has been updated to latest one. 230 | * Fixed the issue #15. Added new parameter User Id (uid). As part of this, another change was made to move initializing the default ClientId parameter from GoogleAnalyticsRequest to DefaultRequest. This way, whatever the default clientid you use, will be used for all requests. Previously, default client id wasn't referred. 231 | 232 | Version 1.1.0 - Apr 22 2014 233 | -- 234 | * Fixed the issue #5. Fix changes some of the existing behavior. If you are using discover system parameters, then by default Screen Colors and Screen Resolution will not be populated. If you would like, you need to set AwtRequestParameterDiscoverer in the GoogleAnalyticsConfig before initializing the GoogleAnalytics. This change is to ensure that it can be used in a environment where JVM has no access to java.awt.* classes. 235 | 236 | Version 1.0.5 - Apr 09 2014 237 | -- 238 | * Fixed the issue #12 239 | 240 | Version 1.0.4 - Mar 3 2014 241 | -- 242 | * Fixed the issue #8 243 | 244 | Version 1.0.3 - Jan 20 2014 245 | -- 246 | * Fixed the issue #6 247 | 248 | 249 | Development Notes 250 | == 251 | * To release (assuming you have maven / sonatype permission), `mvn release:prepare` then `mvn release:perform` 252 | 253 | 254 | Other Implementations 255 | == 256 | 257 | This is a fork of what I still consider the "upstream" version here: https://github.com/brsanthu/google-analytics-java 258 | 259 | Santosh Kumar created what I believe is the best open-source Java google analytics client. My only reason for forking was a desire for a large number of changes rapidly and I didn't see PRs being accepted in the main repo - no other reason and an eventual merge would be fine. In the same manner: please fork this repo and move forward if I don't respond to you :-) 260 | 261 | There are few Java implementation of Google Analytics api, but found some issues (or protocol mismatch) with each of them. 262 | 263 | https://github.com/nhnopensource/universal-analytics-java 264 | * Doesn't implement all parameters of Measurement Protocol. 265 | * Cannot specify default parameters 266 | * Only one unit test case and coverage is very minimal 267 | * Uses Legacy Apache Http Client (3.x) 268 | 269 | https://code.google.com/p/jgoogleanalyticstracker/ 270 | * Implements Legacy Google Analytics protocol 271 | 272 | https://github.com/siddii/jgoogleanalytics 273 | * Implements Legacy Google Analytics protocol 274 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | net.mikehardy 4 | google-analytics-java 5 | 2.0.12-SNAPSHOT 6 | jar 7 | 8 | 9 | 1.8 10 | 2.0.7 11 | 4.13.2 12 | 5.2.0 13 | 4.5.14 14 | 4.10.0 15 | UTF-8 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.sonatype.plugins 23 | nexus-staging-maven-plugin 24 | 1.6.13 25 | true 26 | 27 | ossrh 28 | https://oss.sonatype.org/ 29 | true 30 | 31 | 32 | 33 | 34 | maven-compiler-plugin 35 | 3.11.0 36 | 37 | ${java-version} 38 | ${java-version} 39 | true 40 | true 41 | 42 | 43 | 44 | maven-source-plugin 45 | 3.2.1 46 | 47 | 48 | attach-sources 49 | 50 | jar-no-fork 51 | 52 | 53 | 54 | 55 | 56 | 57 | maven-javadoc-plugin 58 | 3.5.0 59 | 60 | 61 | false 62 | none 63 | 64 | 65 | 66 | attach-javadocs 67 | 68 | jar 69 | 70 | true 71 | 72 | 73 | 74 | 75 | 76 | maven-jar-plugin 77 | 3.3.0 78 | 79 | 80 | true 81 | 82 | true 83 | true 84 | 85 | 86 | 87 | 88 | 89 | 90 | maven-surefire-plugin 91 | 3.0.0 92 | 93 | false 94 | 95 | 96 | 97 | 98 | org.apache.felix 99 | maven-bundle-plugin 100 | 5.1.8 101 | 102 | META-INF 103 | 104 | ${project.groupId}.${project.artifactId} 105 | *;version=! 106 | 107 | 108 | 109 | 110 | bundle-manifest 111 | process-classes 112 | 113 | manifest 114 | 115 | 116 | 117 | bundle 118 | package 119 | 120 | bundle 121 | 122 | 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-release-plugin 129 | 3.0.0 130 | 131 | true 132 | true 133 | release 134 | deploy 135 | 136 | 137 | 138 | org.apache.maven.plugins 139 | maven-gpg-plugin 140 | 3.0.1 141 | 142 | 143 | sign-artifacts 144 | verify 145 | 146 | sign 147 | 148 | 149 | ${gpg.keyname} 150 | ${gpg.keyname} 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | org.slf4j 161 | slf4j-api 162 | ${slf4j-version} 163 | 164 | 165 | org.slf4j 166 | slf4j-simple 167 | ${slf4j-version} 168 | test 169 | 170 | 171 | org.slf4j 172 | jcl-over-slf4j 173 | ${slf4j-version} 174 | 175 | 176 | org.apache.httpcomponents 177 | httpclient 178 | ${httpclient-version} 179 | 180 | 181 | commons-logging 182 | commons-logging 183 | 184 | 185 | 186 | 187 | com.squareup.okhttp3 188 | okhttp 189 | ${okhttp-version} 190 | 191 | 192 | junit 193 | junit 194 | ${junit.version} 195 | test 196 | 197 | 198 | org.mockito 199 | mockito-core 200 | ${mockito.version} 201 | test 202 | 203 | 204 | Google Analytics Java API 205 | https://github.com/mikehardy/google-analytics-java 206 | Open Source Java API for Google Analytics. 207 | 208 | More information about the protocol is available at https://developers.google.com/analytics/devguides/collection/protocol/v1/ 209 | Sep 2013 210 | 211 | http://mikehardy.net 212 | Mike Hardy 213 | 214 | 215 | https://github.com:mikehardy/google-analytics-java 216 | scm:git:git@github.com:mikehardy/google-analytics-java.git 217 | scm:git:git@github.com:mikehardy/google-analytics-java.git 218 | HEAD 219 | 220 | 221 | github.com 222 | https://github.com/mikehardy/google-analytics-java/issues 223 | 224 | 225 | 226 | Santhosh Kumar 227 | http://www.brsanthu.com 228 | http://www.brsanthu.com 229 | 230 | Initiator 231 | Commiter 232 | 233 | brsanthu at gmail 234 | 235 | 236 | Mike Hardy 237 | http://mikehardy.net 238 | http://mikehardy.net 239 | 240 | Commiter 241 | 242 | mikehardy 243 | mike at mikehardy and a dot net 244 | 245 | 246 | 247 | 248 | The Apache Software License, Version 2.0 249 | http://www.apache.org/licenses/LICENSE-2.0.txt 250 | repo 251 | 252 | 253 | 254 | 255 | Travis CI 256 | https://travis-ci.com/mikehardy/google-analytics-java/ 257 | 258 | 259 | 260 | ossrh 261 | https://oss.sonatype.org/content/repositories/snapshots 262 | 263 | 264 | ossrh 265 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/GoogleAnalytics.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import com.brsanthu.googleanalytics.request.EventHit; 4 | import com.brsanthu.googleanalytics.request.ExceptionHit; 5 | import com.brsanthu.googleanalytics.request.ItemHit; 6 | import com.brsanthu.googleanalytics.request.PageViewHit; 7 | import com.brsanthu.googleanalytics.request.ScreenViewHit; 8 | import com.brsanthu.googleanalytics.request.SocialHit; 9 | import com.brsanthu.googleanalytics.request.TimingHit; 10 | import com.brsanthu.googleanalytics.request.TransactionHit; 11 | 12 | public interface GoogleAnalytics extends AutoCloseable { 13 | 14 | EventHit event(); 15 | 16 | ExceptionHit exception(); 17 | 18 | ItemHit item(); 19 | 20 | PageViewHit pageView(); 21 | 22 | PageViewHit pageView(String url, String title); 23 | 24 | PageViewHit pageView(String url, String title, String description); 25 | 26 | ScreenViewHit screenView(); 27 | 28 | ScreenViewHit screenView(String appName, String screenName); 29 | 30 | SocialHit social(); 31 | 32 | SocialHit social(String socialNetwork, String socialAction, String socialTarget); 33 | 34 | TimingHit timing(); 35 | 36 | TransactionHit transaction(); 37 | 38 | GoogleAnalyticsStats getStats(); 39 | 40 | GoogleAnalyticsConfig getConfig(); 41 | 42 | boolean inSample(); 43 | 44 | boolean performSamplingElection(); 45 | 46 | void ifEnabled(Runnable runnable); 47 | 48 | void resetStats(); 49 | 50 | static GoogleAnalyticsBuilder builder() { 51 | return new GoogleAnalyticsBuilder(); 52 | } 53 | 54 | void flush(); 55 | } -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/GoogleAnalyticsBuilder.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import com.brsanthu.googleanalytics.discovery.DefaultRequestParameterDiscoverer; 4 | import com.brsanthu.googleanalytics.discovery.RequestParameterDiscoverer; 5 | import com.brsanthu.googleanalytics.httpclient.ApacheHttpClientImpl; 6 | import com.brsanthu.googleanalytics.httpclient.HttpClient; 7 | import com.brsanthu.googleanalytics.internal.GaUtils; 8 | import com.brsanthu.googleanalytics.internal.GoogleAnalyticsImpl; 9 | import com.brsanthu.googleanalytics.request.DefaultRequest; 10 | 11 | public class GoogleAnalyticsBuilder { 12 | private GoogleAnalyticsConfig config = new GoogleAnalyticsConfig(); 13 | private DefaultRequest defaultRequest = new DefaultRequest(); 14 | private HttpClient httpClient; 15 | 16 | public GoogleAnalyticsBuilder withConfig(GoogleAnalyticsConfig config) { 17 | this.config = GaUtils.firstNotNull(config, new GoogleAnalyticsConfig()); 18 | return this; 19 | } 20 | 21 | public GoogleAnalyticsBuilder withTrackingId(String trackingId) { 22 | defaultRequest.trackingId(trackingId); 23 | return this; 24 | } 25 | 26 | public GoogleAnalyticsBuilder withAppName(String value) { 27 | defaultRequest.applicationName(value); 28 | return this; 29 | } 30 | 31 | public GoogleAnalyticsBuilder withAppVersion(String value) { 32 | defaultRequest.applicationVersion(value); 33 | return this; 34 | } 35 | 36 | public GoogleAnalyticsBuilder withDefaultRequest(DefaultRequest defaultRequest) { 37 | this.defaultRequest = GaUtils.firstNotNull(defaultRequest, new DefaultRequest()); 38 | return this; 39 | } 40 | 41 | public GoogleAnalyticsBuilder withHttpClient(HttpClient httpClient) { 42 | this.httpClient = httpClient; 43 | return this; 44 | } 45 | 46 | public GoogleAnalytics build() { 47 | if (config.isDiscoverRequestParameters()) { 48 | RequestParameterDiscoverer discoverer = GaUtils.firstNotNull(config.getRequestParameterDiscoverer(), 49 | DefaultRequestParameterDiscoverer.INSTANCE); 50 | 51 | discoverer.discoverParameters(config, defaultRequest); 52 | } 53 | 54 | return new GoogleAnalyticsImpl(config, defaultRequest, createHttpClient()); 55 | } 56 | 57 | protected HttpClient createHttpClient() { 58 | if (httpClient != null) { 59 | return httpClient; 60 | } 61 | 62 | return new ApacheHttpClientImpl(config); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/GoogleAnalyticsConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | */ 11 | package com.brsanthu.googleanalytics; 12 | 13 | import com.brsanthu.googleanalytics.discovery.AwtRequestParameterDiscoverer; 14 | import com.brsanthu.googleanalytics.discovery.DefaultRequestParameterDiscoverer; 15 | import com.brsanthu.googleanalytics.discovery.RequestParameterDiscoverer; 16 | import com.brsanthu.googleanalytics.internal.GoogleAnalyticsImpl; 17 | import com.brsanthu.googleanalytics.internal.GoogleAnalyticsStatsImpl; 18 | 19 | /** 20 | * Properties that can be configured in this library. These would include any properties that are required to process 21 | * the tracking request or enhance the tracking request (but not specified in measurement protocol like User agent). 22 | *

23 | * Most of the properties are initialization level and request level. If a property is a initialization level property, 24 | * it should be set at the time of GoogleAnalytics object initialization. If a property is a request level property, it 25 | * can be set any time and it will be effective. 26 | *

27 | * All properties of this config object supports method chaining. So for example, you could do, 28 | * new GoogleAnalyticsConfig().setMaxThreads(2).setThreadNameFormat("name"); 29 | * 30 | * @author Santhosh Kumar 31 | */ 32 | public class GoogleAnalyticsConfig { 33 | 34 | public static final int DEFAULT_MAX_HTTP_CONNECTIONS_PER_ROUTE = 10; 35 | 36 | private String threadNameFormat = "googleanalyticsjava-thread-{0}"; 37 | private boolean enabled = true; 38 | private int minThreads = 0; 39 | private int maxThreads = 5; 40 | private int threadTimeoutSecs = 300; 41 | private int threadQueueSize = 1000; 42 | private int maxHttpConnectionsPerRoute = DEFAULT_MAX_HTTP_CONNECTIONS_PER_ROUTE; 43 | private int samplePercentage = 100; 44 | private boolean useHttps = true; 45 | private boolean validate = true; 46 | private boolean batchingEnabled = false; 47 | private int batchSize = 20; 48 | private String httpUrl = "http://www.google-analytics.com/collect"; 49 | private String httpsUrl = "https://www.google-analytics.com/collect"; 50 | private String batchUrl = "https://www.google-analytics.com/batch"; 51 | private String userAgent = null; 52 | private String proxyHost = null; 53 | private int proxyPort = 80; 54 | private String proxyUserName = null; 55 | private String proxyPassword = null; 56 | private boolean discoverRequestParameters = true; 57 | private boolean gatherStats = false; 58 | private RequestParameterDiscoverer requestParameterDiscoverer = new DefaultRequestParameterDiscoverer(); 59 | 60 | public RequestParameterDiscoverer getRequestParameterDiscoverer() { 61 | return requestParameterDiscoverer; 62 | } 63 | 64 | /** 65 | * Sets the appropriate request parameter discoverer. Default is {@link DefaultRequestParameterDiscoverer} but can 66 | * be changed to {@link AwtRequestParameterDiscoverer} if you want to use Toolkit to derive the screen resolution 67 | * etc. 68 | * 69 | * Please make sure you also enable the discovery using {@link #setDiscoverRequestParameters(boolean)} 70 | * 71 | * @param requestParameterDiscoverer can be null and is so, parameters will not be discovered. 72 | * @return GoogleAnalyticsConfig with requestParameterDiscoverer set 73 | */ 74 | public GoogleAnalyticsConfig setRequestParameterDiscoverer(RequestParameterDiscoverer requestParameterDiscoverer) { 75 | this.requestParameterDiscoverer = requestParameterDiscoverer; 76 | return this; 77 | } 78 | 79 | public boolean isGatherStats() { 80 | return gatherStats; 81 | } 82 | 83 | /** 84 | * If set to true, {@link GoogleAnalyticsImpl} will collect the basic stats about successful event postings for 85 | * various hit types and keeps a copy of {@link GoogleAnalyticsStatsImpl}, which can be retrieved using 86 | * {@link GoogleAnalyticsImpl#getStats()} 87 | * 88 | * @param gatherStats 89 | * @return GoogleAnalyticsConfig with gatherStats set 90 | */ 91 | public GoogleAnalyticsConfig setGatherStats(boolean gatherStats) { 92 | this.gatherStats = gatherStats; 93 | return this; 94 | } 95 | 96 | /** 97 | * Sets the thread name format that should be while creating the threads. 98 | *

99 | * Default is "googleanalytics-thread-{0}" where {0} is the thread counter. If you specify a custom format, make 100 | * sure {0} is there somewhere otherwise all threads will be nameed same and can be an issue for troubleshooting. 101 | * 102 | * @param threadNameFormat non-null string for thread name. 103 | */ 104 | public GoogleAnalyticsConfig setThreadNameFormat(String threadNameFormat) { 105 | this.threadNameFormat = threadNameFormat; 106 | return this; 107 | } 108 | 109 | public String getThreadNameFormat() { 110 | return threadNameFormat; 111 | } 112 | 113 | /** 114 | * Deprecated since 1.0.6 115 | * 116 | * @deprecated Use {@link #setDiscoverRequestParameters(boolean)} instead 117 | */ 118 | @Deprecated 119 | public GoogleAnalyticsConfig setDeriveSystemParameters(boolean deriveSystemProperties) { 120 | return setDiscoverRequestParameters(deriveSystemProperties); 121 | } 122 | 123 | /** 124 | * If true, derives the system properties (User Language, Region, Country, Screen Size, Color Depth, and File 125 | * encoding) and adds to the default request. 126 | * 127 | *

128 | * This is initialization level configuration (must be set while creating GoogleAnalytics object). 129 | *

130 | */ 131 | public GoogleAnalyticsConfig setDiscoverRequestParameters(boolean discoverSystemParameters) { 132 | this.discoverRequestParameters = discoverSystemParameters; 133 | return this; 134 | } 135 | 136 | public boolean isDiscoverRequestParameters() { 137 | return discoverRequestParameters; 138 | } 139 | 140 | /** 141 | * Sets the user name which should be used to authenticate to the proxy server. This is applicable only if 142 | * {@link #setProxyHost(String)} is not empty. 143 | * 144 | *

145 | * This is initialization level configuration (must be set while creating GoogleAnalytics object). 146 | *

147 | */ 148 | public GoogleAnalyticsConfig setProxyUserName(String proxyUserName) { 149 | this.proxyUserName = proxyUserName; 150 | return this; 151 | } 152 | 153 | public String getProxyUserName() { 154 | return proxyUserName; 155 | } 156 | 157 | public String getProxyPassword() { 158 | return proxyPassword; 159 | } 160 | 161 | /** 162 | * Sets the password which should be used to authenticate to the proxy server. This is applicable only if 163 | * {@link #setProxyHost(String)} and {@link #setProxyUserName(String)} is not empty. 164 | * 165 | *

166 | * This is initialization level configuration (must be set while creating GoogleAnalytics object). 167 | *

168 | */ 169 | public GoogleAnalyticsConfig setProxyPassword(String proxyPassword) { 170 | this.proxyPassword = proxyPassword; 171 | return this; 172 | } 173 | 174 | public String getProxyHost() { 175 | return proxyHost; 176 | } 177 | 178 | /** 179 | * Sets the host name of the proxy server, to connect to Google analytics. 180 | * 181 | *

182 | * This is initialization level configuration (must be set while creating GoogleAnalytics object). 183 | *

184 | */ 185 | public GoogleAnalyticsConfig setProxyHost(String proxyHost) { 186 | this.proxyHost = proxyHost; 187 | return this; 188 | } 189 | 190 | public int getProxyPort() { 191 | return proxyPort; 192 | } 193 | 194 | /** 195 | * Sets the host name of the proxy server, to connect to Google analytics. 196 | * 197 | *

198 | * This is initialization level configuration (must be set while creating GoogleAnalytics object). 199 | *

200 | */ 201 | public GoogleAnalyticsConfig setProxyPort(int proxyPort) { 202 | this.proxyPort = proxyPort; 203 | return this; 204 | } 205 | 206 | public String getUserAgent() { 207 | return userAgent; 208 | } 209 | 210 | /** 211 | * Sets the user agent string that should be sent while making the http request. Default is Apache Http Client's 212 | * user agent, which looks something similar to this. Apache-HttpClient/release (java 1.5) 213 | * 214 | *

215 | * This is initialization level configuration (must be set while creating GoogleAnalytics object). 216 | *

217 | */ 218 | public GoogleAnalyticsConfig setUserAgent(String userAgent) { 219 | this.userAgent = userAgent; 220 | return this; 221 | } 222 | 223 | public boolean isEnabled() { 224 | return enabled; 225 | } 226 | 227 | /** 228 | * Enables or disables the GoogleAnalytics posting. If disabled, library will continue to accept the send/post 229 | * requests but silently skips sending the event and returns successful response. Default is false. 230 | * 231 | *

232 | * This is request level configuration (can be changed any time). 233 | *

234 | */ 235 | public GoogleAnalyticsConfig setEnabled(boolean enabled) { 236 | this.enabled = enabled; 237 | return this; 238 | } 239 | 240 | /** 241 | * Maximum threads to use to process the asynchronous event posting and Http client connection pooling. Default is 242 | * 243 | *

244 | * This is initialization level configuration (must be set while creating GoogleAnalytics object). 245 | *

246 | */ 247 | public int getMaxThreads() { 248 | return maxThreads; 249 | } 250 | 251 | public GoogleAnalyticsConfig setMaxThreads(int maxThreads) { 252 | this.maxThreads = maxThreads; 253 | return this; 254 | } 255 | 256 | public int getMinThreads() { 257 | return minThreads; 258 | } 259 | 260 | public GoogleAnalyticsConfig setMinThreads(int minThreads) { 261 | this.minThreads = minThreads; 262 | return this; 263 | } 264 | 265 | public boolean isUseHttps() { 266 | return useHttps; 267 | } 268 | 269 | /** 270 | * Instructs to use https url to send the events. Default is true. 271 | * 272 | *

273 | * This is request level configuration (can be changed any time). 274 | *

275 | */ 276 | public GoogleAnalyticsConfig setUseHttps(boolean useHttps) { 277 | this.useHttps = useHttps; 278 | return this; 279 | } 280 | 281 | public boolean isValidate() { 282 | return validate; 283 | } 284 | 285 | /** 286 | * If set, validates the request before sending to Google Analytics. If any errors found, GoogleAnalyticsException 287 | * will be thrown with details. Default is false. Note that, if you are sending the event in async mode, then 288 | * request is always validated and logged to log file as warnings irrespective of this flag. 289 | * 290 | *

291 | * This is request level configuration (can be changed any time). 292 | *

293 | */ 294 | public GoogleAnalyticsConfig setValidate(boolean validate) { 295 | this.validate = validate; 296 | return this; 297 | } 298 | 299 | public String getHttpUrl() { 300 | return httpUrl; 301 | } 302 | 303 | /** 304 | * URL to use when posting the event in http mode. This url is Google Analytics service url and usually not updated 305 | * by the clients. 306 | * 307 | *

308 | * Default value is http://www.google-analytics.com/collect 309 | *

310 | * 311 | *

312 | * This is request level configuration (can be changed any time). 313 | *

314 | */ 315 | public GoogleAnalyticsConfig setHttpUrl(String httpUrl) { 316 | this.httpUrl = httpUrl; 317 | return this; 318 | } 319 | 320 | public String getHttpsUrl() { 321 | return httpsUrl; 322 | } 323 | 324 | /** 325 | * URL to use when posting the event in https mode. This url is Google Analytics service url and usually not updated 326 | * by the clients. 327 | *

328 | * Default value is https://www.google-analytics.com/collect 329 | * 330 | *

331 | * This is request level configuration (can be changed any time). 332 | *

333 | */ 334 | public GoogleAnalyticsConfig setHttpsUrl(String httpsUrl) { 335 | this.httpsUrl = httpsUrl; 336 | return this; 337 | } 338 | 339 | public String getUrl() { 340 | return useHttps ? httpsUrl : httpUrl; 341 | } 342 | 343 | public int getMaxHttpConnectionsPerRoute() { 344 | return maxHttpConnectionsPerRoute; 345 | } 346 | 347 | public GoogleAnalyticsConfig setMaxHttpConnectionsPerRoute(int maxHttpConnectionsPerRoute) { 348 | this.maxHttpConnectionsPerRoute = maxHttpConnectionsPerRoute; 349 | return this; 350 | } 351 | 352 | /** 353 | * The sample percentage to apply to all analytics. Integer between 0 and 100, every time an analytics implementation 354 | * is initialized, a random number will be compared to the sample percentage, allowing or disallowing analytics 355 | * from the initialized instance 356 | * @return int between 1 and 100 357 | */ 358 | public int getSamplePercentage() { 359 | return samplePercentage; 360 | } 361 | 362 | public GoogleAnalyticsConfig setSamplePercentage(int samplePercentage) { 363 | this.samplePercentage = samplePercentage; 364 | return this; 365 | } 366 | 367 | @Override 368 | public String toString() { 369 | return "GoogleAnalyticsConfig [threadNameFormat=" + threadNameFormat + ", enabled=" + enabled + ", minThreads=" + minThreads + ", maxThreads=" 370 | + maxThreads + ", threadTimeoutSecs=" + threadTimeoutSecs + ", threadQueueSize=" + threadQueueSize + ", maxHttpConnectionsPerRoute=" 371 | + maxHttpConnectionsPerRoute + ", samplePercentage=" + samplePercentage + ", useHttps=" + useHttps + ", validate=" + validate + ", httpUrl=" + httpUrl + ", httpsUrl=" + httpsUrl 372 | + ", userAgent=" + userAgent + ", proxyHost=" + proxyHost + ", proxyPort=" + proxyPort + ", proxyUserName=" + proxyUserName 373 | + ", proxyPassword=" + mask(proxyPassword) + ", discoverRequestParameters=" + discoverRequestParameters + ", gatherStats=" 374 | + gatherStats + ", requestParameterDiscoverer=" + requestParameterDiscoverer + "]"; 375 | } 376 | 377 | public static String mask(String value) { 378 | return value == null ? null : "********"; 379 | } 380 | 381 | public int getThreadQueueSize() { 382 | return threadQueueSize; 383 | } 384 | 385 | public GoogleAnalyticsConfig setThreadQueueSize(int threadQueueSize) { 386 | this.threadQueueSize = threadQueueSize; 387 | return this; 388 | } 389 | 390 | public int getThreadTimeoutSecs() { 391 | return threadTimeoutSecs; 392 | } 393 | 394 | public GoogleAnalyticsConfig setThreadTimeoutSecs(int threadTimeoutSecs) { 395 | this.threadTimeoutSecs = threadTimeoutSecs; 396 | return this; 397 | } 398 | 399 | public String getBatchUrl() { 400 | return batchUrl; 401 | } 402 | 403 | public GoogleAnalyticsConfig setBatchUrl(String batchUrl) { 404 | this.batchUrl = batchUrl; 405 | return this; 406 | } 407 | 408 | public boolean isBatchingEnabled() { 409 | return batchingEnabled; 410 | } 411 | 412 | public GoogleAnalyticsConfig setBatchingEnabled(boolean batchingEnabled) { 413 | this.batchingEnabled = batchingEnabled; 414 | return this; 415 | } 416 | 417 | public int getBatchSize() { 418 | return batchSize; 419 | } 420 | 421 | public GoogleAnalyticsConfig setBatchSize(int batchSize) { 422 | this.batchSize = batchSize; 423 | return this; 424 | } 425 | 426 | } 427 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/GoogleAnalyticsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package com.brsanthu.googleanalytics; 16 | 17 | /** 18 | * Any exception thrown (usually due to validation), it would be of this type. 19 | * 20 | * @author Santhosh Kumar 21 | */ 22 | public class GoogleAnalyticsException extends RuntimeException { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | public GoogleAnalyticsException() { 27 | super(); 28 | } 29 | 30 | public GoogleAnalyticsException(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public GoogleAnalyticsException(String message) { 35 | super(message); 36 | } 37 | 38 | public GoogleAnalyticsException(Throwable cause) { 39 | super(cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/GoogleAnalyticsExecutor.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsRequest; 6 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsResponse; 7 | 8 | public interface GoogleAnalyticsExecutor { 9 | GoogleAnalyticsResponse post(GoogleAnalyticsRequest request); 10 | 11 | CompletableFuture postAsync(GoogleAnalyticsRequest request); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/GoogleAnalyticsStats.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | public interface GoogleAnalyticsStats { 4 | 5 | long getPageViewHits(); 6 | 7 | long getEventHits(); 8 | 9 | long getScreenViewHits(); 10 | 11 | long getItemHits(); 12 | 13 | long getTransactionHits(); 14 | 15 | long getTimingHits(); 16 | 17 | long getSocialHits(); 18 | 19 | long getExceptionHits(); 20 | } -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/discovery/AwtRequestParameterDiscoverer.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.discovery; 2 | 3 | import static com.brsanthu.googleanalytics.internal.GaUtils.isEmpty; 4 | 5 | import java.awt.Dimension; 6 | import java.awt.GraphicsDevice; 7 | import java.awt.GraphicsEnvironment; 8 | import java.awt.Toolkit; 9 | 10 | import com.brsanthu.googleanalytics.GoogleAnalyticsConfig; 11 | import com.brsanthu.googleanalytics.request.DefaultRequest; 12 | 13 | /** 14 | * Clases uses AWT classes to discover following properties. 15 | * 19 | * 20 | * @author Santhosh Kumar 21 | */ 22 | public class AwtRequestParameterDiscoverer extends DefaultRequestParameterDiscoverer { 23 | 24 | @Override 25 | public DefaultRequest discoverParameters(GoogleAnalyticsConfig config, DefaultRequest request) { 26 | super.discoverParameters(config, request); 27 | 28 | Toolkit toolkit = Toolkit.getDefaultToolkit(); 29 | 30 | if (isEmpty(request.screenResolution())) { 31 | Dimension screenSize = toolkit.getScreenSize(); 32 | request.screenResolution( 33 | ((int) screenSize.getWidth()) + "x" + ((int) screenSize.getHeight()) + ", " + toolkit.getScreenResolution() + " dpi"); 34 | } 35 | 36 | if (isEmpty(request.screenColors())) { 37 | GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); 38 | GraphicsDevice[] graphicsDevices = graphicsEnvironment.getScreenDevices(); 39 | 40 | StringBuilder sb = new StringBuilder(); 41 | for (GraphicsDevice graphicsDevice : graphicsDevices) { 42 | if (sb.length() != 0) { 43 | sb.append(", "); 44 | } 45 | sb.append(graphicsDevice.getDisplayMode().getBitDepth()); 46 | } 47 | request.screenColors(sb.toString()); 48 | } 49 | 50 | return request; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/discovery/DefaultRequestParameterDiscoverer.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.discovery; 2 | 3 | import static com.brsanthu.googleanalytics.internal.GaUtils.appendSystemProperty; 4 | import static com.brsanthu.googleanalytics.internal.GaUtils.isEmpty; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.brsanthu.googleanalytics.GoogleAnalyticsConfig; 10 | import com.brsanthu.googleanalytics.request.DefaultRequest; 11 | 12 | /** 13 | * Default request parameter discoverer. Discovers following parameters. 14 | * 20 | * 21 | * @author Santhosh Kumar 22 | */ 23 | public class DefaultRequestParameterDiscoverer implements RequestParameterDiscoverer { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(DefaultRequestParameterDiscoverer.class); 26 | public static final DefaultRequestParameterDiscoverer INSTANCE = new DefaultRequestParameterDiscoverer(); 27 | 28 | @Override 29 | public DefaultRequest discoverParameters(GoogleAnalyticsConfig config, DefaultRequest request) { 30 | try { 31 | if (isEmpty(config.getUserAgent())) { 32 | config.setUserAgent(getUserAgentString()); 33 | } 34 | 35 | if (isEmpty(request.userLanguage())) { 36 | String region = System.getProperty("user.region"); 37 | if (isEmpty(region)) { 38 | region = System.getProperty("user.country"); 39 | } 40 | request.userLanguage(System.getProperty("user.language") + "-" + region); 41 | } 42 | 43 | if (isEmpty(request.documentEncoding())) { 44 | request.documentEncoding(System.getProperty("file.encoding")); 45 | } 46 | 47 | } catch (Exception e) { 48 | logger.warn("Exception while deriving the System properties for request " + request, e); 49 | } 50 | 51 | return request; 52 | } 53 | 54 | protected String getUserAgentString() { 55 | StringBuilder sb = new StringBuilder("java"); 56 | appendSystemProperty(sb, "java.runtime.version"); 57 | appendSystemProperty(sb, "java.specification.vendor"); 58 | appendSystemProperty(sb, "java.vm.name"); 59 | appendSystemProperty(sb, "os.name"); 60 | appendSystemProperty(sb, "os.version"); 61 | appendSystemProperty(sb, "os.arch"); 62 | 63 | return sb.toString(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/discovery/RequestParameterDiscoverer.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.discovery; 2 | 3 | import com.brsanthu.googleanalytics.GoogleAnalyticsConfig; 4 | import com.brsanthu.googleanalytics.request.DefaultRequest; 5 | 6 | /** 7 | * Mechanism to discover some default request parameters. 8 | */ 9 | public interface RequestParameterDiscoverer { 10 | 11 | public DefaultRequest discoverParameters(GoogleAnalyticsConfig config, DefaultRequest request); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/ApacheHttpClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | import static com.brsanthu.googleanalytics.internal.GaUtils.isNotEmpty; 4 | 5 | import java.io.IOException; 6 | import java.net.UnknownHostException; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.List; 9 | import java.util.concurrent.*; 10 | import java.util.stream.Collectors; 11 | 12 | import org.apache.http.HttpEntity; 13 | import org.apache.http.HttpHost; 14 | import org.apache.http.NameValuePair; 15 | import org.apache.http.auth.AuthScope; 16 | import org.apache.http.auth.UsernamePasswordCredentials; 17 | import org.apache.http.client.ClientProtocolException; 18 | import org.apache.http.client.entity.UrlEncodedFormEntity; 19 | import org.apache.http.client.methods.CloseableHttpResponse; 20 | import org.apache.http.client.methods.HttpPost; 21 | import org.apache.http.impl.client.BasicCredentialsProvider; 22 | import org.apache.http.impl.client.CloseableHttpClient; 23 | import org.apache.http.impl.client.HttpClientBuilder; 24 | import org.apache.http.impl.client.HttpClients; 25 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 26 | import org.apache.http.message.BasicNameValuePair; 27 | import org.apache.http.util.EntityUtils; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | import com.brsanthu.googleanalytics.GoogleAnalyticsConfig; 32 | 33 | public class ApacheHttpClientImpl implements HttpClient { 34 | private static final Logger logger = LoggerFactory.getLogger(ApacheHttpClientImpl.class); 35 | 36 | private CloseableHttpClient apacheHttpClient; 37 | protected final ExecutorService executor; 38 | 39 | public ApacheHttpClientImpl(GoogleAnalyticsConfig config) { 40 | this.apacheHttpClient = createHttpClient(config); 41 | this.executor = createExecutor(config); 42 | } 43 | 44 | @Override 45 | public void close() { 46 | try { 47 | apacheHttpClient.close(); 48 | } catch (IOException e) { 49 | // ignore 50 | } 51 | } 52 | 53 | protected CloseableHttpClient createHttpClient(GoogleAnalyticsConfig config) { 54 | PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); 55 | connManager.setDefaultMaxPerRoute(Math.max(config.getMaxHttpConnectionsPerRoute(), 1)); 56 | 57 | HttpClientBuilder builder = HttpClients.custom().setConnectionManager(connManager); 58 | 59 | if (isNotEmpty(config.getUserAgent())) { 60 | builder.setUserAgent(config.getUserAgent()); 61 | } 62 | 63 | if (isNotEmpty(config.getProxyHost())) { 64 | builder.setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort())); 65 | 66 | if (isNotEmpty(config.getProxyUserName())) { 67 | BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 68 | credentialsProvider.setCredentials(new AuthScope(config.getProxyHost(), config.getProxyPort()), 69 | new UsernamePasswordCredentials(config.getProxyUserName(), config.getProxyPassword())); 70 | builder.setDefaultCredentialsProvider(credentialsProvider); 71 | } 72 | } 73 | 74 | return builder.build(); 75 | } 76 | 77 | @Override 78 | public boolean isBatchSupported() { 79 | return true; 80 | } 81 | 82 | protected CloseableHttpResponse execute(String url, HttpEntity entity) throws ClientProtocolException, IOException { 83 | 84 | HttpPost httpPost = new HttpPost(url); 85 | 86 | httpPost.setEntity(entity); 87 | 88 | return apacheHttpClient.execute(httpPost); 89 | } 90 | 91 | protected List createNameValuePairs(HttpRequest req) { 92 | List params = new CopyOnWriteArrayList<>(); 93 | req.getBodyParams().forEach((key, value) -> params.add(new BasicNameValuePair(key, value))); 94 | return params; 95 | } 96 | 97 | @Override 98 | public CompletableFuture post(HttpRequest req) { 99 | return CompletableFuture.supplyAsync(() -> { 100 | HttpResponse resp = new HttpResponse(); 101 | CloseableHttpResponse httpResp = null; 102 | 103 | try { 104 | 105 | httpResp = execute(req.getUrl(), new UrlEncodedFormEntity(createNameValuePairs(req), StandardCharsets.UTF_8)); 106 | resp.setStatusCode(httpResp.getStatusLine().getStatusCode()); 107 | 108 | } catch (Exception e) { 109 | if (e instanceof UnknownHostException) { 110 | logger.warn("Couldn't connect to Google Analytics. Internet may not be available. " + e.toString()); 111 | } else { 112 | logger.warn("Exception while sending the Google Analytics tracker request " + req, e); 113 | } 114 | 115 | } finally { 116 | EntityUtils.consumeQuietly(httpResp.getEntity()); 117 | try { 118 | httpResp.close(); 119 | } catch (Exception e2) { 120 | // ignore 121 | } 122 | } 123 | 124 | return (resp); 125 | }); 126 | } 127 | 128 | @Override 129 | public CompletableFuture postBatch(HttpBatchRequest req) { 130 | return CompletableFuture.supplyAsync(() -> { 131 | HttpBatchResponse resp = new HttpBatchResponse(); 132 | CloseableHttpResponse httpResp = null; 133 | 134 | try { 135 | List reqs = new CopyOnWriteArrayList(req.getRequests()); 136 | List> listOfReqPairs = reqs.stream().map(this::createNameValuePairs).collect(Collectors.toList()); 137 | httpResp = execute(req.getUrl(), new BatchUrlEncodedFormEntity(listOfReqPairs)); 138 | resp.setStatusCode(httpResp.getStatusLine().getStatusCode()); 139 | 140 | } catch (Exception e) { 141 | if (e instanceof UnknownHostException) { 142 | logger.warn("Couldn't connect to Google Analytics. Internet may not be available. " + e.toString()); 143 | } else { 144 | logger.warn("Exception while sending the Google Analytics tracker request " + req, e); 145 | } 146 | 147 | } finally { 148 | EntityUtils.consumeQuietly(httpResp.getEntity()); 149 | try { 150 | httpResp.close(); 151 | } catch (Exception e2) { 152 | // ignore 153 | } 154 | } 155 | 156 | return resp; 157 | }); 158 | } 159 | 160 | protected ExecutorService createExecutor(GoogleAnalyticsConfig config) { 161 | if (executor != null) { 162 | return executor; 163 | } 164 | return new ThreadPoolExecutor(config.getMinThreads(), config.getMaxThreads(), config.getThreadTimeoutSecs(), TimeUnit.SECONDS, 165 | new LinkedBlockingDeque(config.getThreadQueueSize()), createThreadFactory(config), new ThreadPoolExecutor.CallerRunsPolicy()); 166 | } 167 | protected ThreadFactory createThreadFactory(GoogleAnalyticsConfig config) { 168 | return new GoogleAnalyticsThreadFactory(config.getThreadNameFormat()); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/BatchUrlEncodedFormEntity.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.List; 5 | 6 | import org.apache.http.NameValuePair; 7 | import org.apache.http.client.utils.URLEncodedUtils; 8 | import org.apache.http.entity.ContentType; 9 | import org.apache.http.entity.StringEntity; 10 | 11 | public class BatchUrlEncodedFormEntity extends StringEntity { 12 | 13 | public BatchUrlEncodedFormEntity(List> parameters) { 14 | super(constructCombinedEntityString(parameters), ContentType.create(URLEncodedUtils.CONTENT_TYPE)); 15 | } 16 | 17 | private static String constructCombinedEntityString(final List> parameters) { 18 | StringBuilder builder = new StringBuilder(); 19 | 20 | for (List param : parameters) { 21 | builder.append(URLEncodedUtils.format(param, StandardCharsets.UTF_8)); 22 | builder.append("\r\n"); 23 | } 24 | 25 | return builder.toString(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/GoogleAnalyticsThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | import java.text.MessageFormat; 4 | import java.util.concurrent.ThreadFactory; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | public class GoogleAnalyticsThreadFactory implements ThreadFactory { 8 | private final AtomicInteger threadNumber = new AtomicInteger(1); 9 | private String threadNameFormat = null; 10 | 11 | public GoogleAnalyticsThreadFactory(String threadNameFormat) { 12 | this.threadNameFormat = threadNameFormat; 13 | } 14 | 15 | @Override 16 | public Thread newThread(Runnable r) { 17 | Thread thread = new Thread(Thread.currentThread().getThreadGroup(), r, MessageFormat.format(threadNameFormat, threadNumber.getAndIncrement()), 18 | 0); 19 | thread.setDaemon(true); 20 | thread.setPriority(Thread.MIN_PRIORITY); 21 | return thread; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/HttpBatchRequest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class HttpBatchRequest { 7 | private String url; 8 | private List requests = new ArrayList<>(); 9 | 10 | public HttpBatchRequest addRequest(HttpRequest request) { 11 | requests.add(request); 12 | return this; 13 | } 14 | 15 | public List getRequests() { 16 | return requests; 17 | } 18 | 19 | public HttpBatchRequest setRequests(List requests) { 20 | this.requests = requests; 21 | return this; 22 | } 23 | 24 | public String getUrl() { 25 | return url; 26 | } 27 | 28 | public HttpBatchRequest setUrl(String url) { 29 | this.url = url; 30 | return this; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/HttpBatchResponse.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | public class HttpBatchResponse { 4 | private int statusCode; 5 | 6 | public int getStatusCode() { 7 | return statusCode; 8 | } 9 | 10 | public HttpBatchResponse setStatusCode(int statusCode) { 11 | this.statusCode = statusCode; 12 | return this; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/HttpClient.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | public interface HttpClient extends AutoCloseable { 6 | CompletableFuture post(HttpRequest req); 7 | 8 | boolean isBatchSupported(); 9 | 10 | CompletableFuture postBatch(HttpBatchRequest req); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/HttpRequest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | import java.util.TreeMap; 4 | import java.util.Map; 5 | 6 | public class HttpRequest { 7 | private String contentType; 8 | private String method; 9 | private String url; 10 | private Map bodyParams = new TreeMap<>(); 11 | 12 | public HttpRequest(String url) { 13 | this.setUrl(url); 14 | } 15 | 16 | public HttpRequest post() { 17 | setMethod("POST"); 18 | return this; 19 | } 20 | 21 | public HttpRequest addBodyParam(String key, String value) { 22 | bodyParams.put(key, value); 23 | return this; 24 | } 25 | 26 | public Map getBodyParams() { 27 | return bodyParams; 28 | } 29 | 30 | public String getContentType() { 31 | return contentType; 32 | } 33 | 34 | public HttpRequest setContentType(String contentType) { 35 | this.contentType = contentType; 36 | return this; 37 | } 38 | 39 | public String getMethod() { 40 | return method; 41 | } 42 | 43 | public HttpRequest setMethod(String method) { 44 | this.method = method; 45 | return this; 46 | } 47 | 48 | public String getUrl() { 49 | return url; 50 | } 51 | 52 | public HttpRequest setUrl(String url) { 53 | this.url = url; 54 | return this; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/HttpResponse.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | public class HttpResponse { 4 | 5 | private int statusCode; 6 | 7 | public int getStatusCode() { 8 | return statusCode; 9 | } 10 | 11 | public HttpResponse setStatusCode(int statusCode) { 12 | this.statusCode = statusCode; 13 | return this; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/NameValuePair.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | public interface NameValuePair { 4 | 5 | String getName(); 6 | 7 | String getValue(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/httpclient/OkHttpClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.httpclient; 2 | 3 | import static com.brsanthu.googleanalytics.internal.GaUtils.isNotEmpty; 4 | 5 | import java.io.IOException; 6 | import java.net.InetSocketAddress; 7 | import java.net.Proxy; 8 | import java.nio.charset.Charset; 9 | import java.util.Map; 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.brsanthu.googleanalytics.GoogleAnalyticsConfig; 16 | 17 | import okhttp3.Authenticator; 18 | import okhttp3.Call; 19 | import okhttp3.Callback; 20 | import okhttp3.Credentials; 21 | import okhttp3.FormBody; 22 | import okhttp3.OkHttpClient; 23 | import okhttp3.Response; 24 | import okhttp3.Route; 25 | import okhttp3.Request; 26 | import okhttp3.RequestBody; 27 | 28 | public class OkHttpClientImpl implements HttpClient { 29 | private static final Logger logger = LoggerFactory.getLogger(OkHttpClientImpl.class); 30 | 31 | // Recommended to be singleton 32 | private static OkHttpClient client; 33 | 34 | public OkHttpClientImpl(GoogleAnalyticsConfig config) { 35 | if (client == null) { 36 | client = createHttpClient(config); 37 | } 38 | } 39 | 40 | @Override 41 | public void close() throws Exception { 42 | // no close methods on OkHttp 43 | } 44 | 45 | /** 46 | * HTTP Client that uses the OkHttp library for request/response. Not 47 | * all GoogleAnalyticsCnofig options are supported. All unsupported 48 | * options in the configuration will be checked on first usage and a 49 | * RuntimeException will be thrown, so you may know your configuration is valid 50 | * after making its first request. 51 | * 52 | * @param config the configuration to use while building OkHttpClient 53 | * @return OkHttpClient to use for analytics hits 54 | * @throws RuntimeException if unsupported configurations are specified 55 | */ 56 | protected OkHttpClient createHttpClient(GoogleAnalyticsConfig config) throws RuntimeException { 57 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 58 | 59 | checkConfigForUnsupportedConfiguration(config); 60 | 61 | if (isNotEmpty(config.getProxyHost())) { 62 | builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.getProxyHost(), config.getProxyPort()))); 63 | 64 | if (isNotEmpty(config.getProxyUserName())) { 65 | Authenticator proxyAuthenticator = new Authenticator() { 66 | 67 | @Override 68 | public Request authenticate(Route route, Response response) throws IOException { 69 | String credential = Credentials.basic(config.getProxyUserName(), config.getProxyPassword()); 70 | 71 | return response.request().newBuilder().header("Proxy-Authorization", credential).build(); 72 | } 73 | }; 74 | builder.proxyAuthenticator(proxyAuthenticator); 75 | } 76 | } 77 | 78 | return builder.build(); 79 | } 80 | 81 | /** 82 | * OkHttp does not support everything Apache HttpClient supports at the library 83 | * level. Additionally, this implementation doesn't implement everything 84 | * supported in the ApacheHttpClientImpl 85 | * 86 | * This checks for unsupported configurations and throws immediately so 87 | * validation will occur at least on the first attempt to use the library 88 | * 89 | * @param config the configuration to check 90 | * @throws RuntimeException if any unsupported configurations are specified 91 | */ 92 | private void checkConfigForUnsupportedConfiguration(GoogleAnalyticsConfig config) throws RuntimeException { 93 | 94 | if (config.getMaxHttpConnectionsPerRoute() != GoogleAnalyticsConfig.DEFAULT_MAX_HTTP_CONNECTIONS_PER_ROUTE) { 95 | throw new RuntimeException("Configuring maximum connections per route is not supported by OkHttp"); 96 | } 97 | if (isNotEmpty(config.getUserAgent())) { 98 | throw new RuntimeException("GoogleAnalyticsConfig.userAgent is not currently supported"); 99 | } 100 | } 101 | 102 | @Override 103 | public CompletableFuture post(HttpRequest req) { 104 | FormBody.Builder formBuilder = new FormBody.Builder(Charset.forName("UTF-8")); 105 | Map reqParams = req.getBodyParams(); 106 | for (String key : reqParams.keySet()) { 107 | if (logger.isTraceEnabled()) logger.trace("post() adding POST param " + key + " = " + reqParams.get(key)); 108 | formBuilder.add(key, reqParams.get(key)); 109 | } 110 | RequestBody body = formBuilder.build(); 111 | 112 | Request request = new Request.Builder().url(req.getUrl()).post(body).build(); 113 | if (logger.isDebugEnabled()) logger.debug("HttpClient.post() url/body: " + request.url() + " / " + renderBody(body)); 114 | 115 | CompletableFuture completableFuture = new CompletableFuture<>(); 116 | 117 | client.newCall(request).enqueue(new Callback() { 118 | @Override 119 | public void onFailure(Call call, IOException e) { 120 | logger.warn("OkHttpClientImpl.post()/OkHttpClient.newCall() error", e); 121 | completableFuture.completeExceptionally(e); 122 | } 123 | 124 | @Override 125 | public void onResponse(Call call, Response okResp) { 126 | if (logger.isDebugEnabled()) logger.debug("post() response code/success: " + okResp.code() + " / " + okResp.isSuccessful()); 127 | HttpResponse resp = new HttpResponse(); 128 | resp.setStatusCode(okResp.code()); 129 | okResp.close(); // this marks the response as consumed, and allows the connection to be re-used 130 | completableFuture.complete(resp); 131 | } 132 | }); 133 | 134 | return completableFuture; 135 | } 136 | 137 | @Override 138 | public CompletableFuture postBatch(HttpBatchRequest batchReq) { 139 | final okio.Buffer bodyBuffer = new okio.Buffer(); 140 | 141 | // For each request in the batch, build up the encoded form string, and add it 142 | // to the buffer 143 | for (HttpRequest request : batchReq.getRequests()) { 144 | FormBody.Builder formBuilder = new FormBody.Builder(Charset.forName("UTF-8")); 145 | if (logger.isTraceEnabled()) logger.trace("postBatch() starting new request line"); 146 | Map reqParams = request.getBodyParams(); 147 | for (String key : reqParams.keySet()) { 148 | if (logger.isTraceEnabled()) logger.trace("postBatch() adding POST param " + key + " = " + reqParams.get(key)); 149 | formBuilder.add(key, reqParams.get(key)); 150 | } 151 | if (logger.isTraceEnabled()) logger.trace("postBatch() finishing request line"); 152 | 153 | try { 154 | formBuilder.build().writeTo(bodyBuffer); 155 | } catch (IOException ioe) { 156 | logger.warn("postBatch() error while rendering batch entry", ioe); 157 | } 158 | bodyBuffer.writeString("\r\n", Charset.forName("UTF-8")); 159 | } 160 | 161 | Request request = new Request.Builder().url(batchReq.getUrl()).post(RequestBody.create(bodyBuffer.readUtf8(), null)).build(); 162 | if (logger.isDebugEnabled()) logger.debug("HttpClient.postBatch() url/body: " + request.url() + " / " + renderBody(request.body())); 163 | 164 | CompletableFuture completableFuture = new CompletableFuture<>(); 165 | client.newCall(request).enqueue(new Callback() { 166 | @Override 167 | public void onFailure(Call call, IOException e) { 168 | logger.warn("OkHttpClientImpl.postBatch()/OkHttpClient.newCall() error", e); 169 | completableFuture.completeExceptionally(e); 170 | } 171 | 172 | @Override 173 | public void onResponse(Call call, Response okResp) { 174 | if (logger.isDebugEnabled()) logger.debug("postBatch() response code/success: " + okResp.code() + " / " + okResp.isSuccessful()); 175 | HttpBatchResponse resp = new HttpBatchResponse(); 176 | resp.setStatusCode(okResp.code()); 177 | okResp.close(); // this marks the response as consumed, and allows the connection to be re-used 178 | completableFuture.complete(resp); 179 | } 180 | }); 181 | 182 | return completableFuture; 183 | } 184 | 185 | @Override 186 | public boolean isBatchSupported() { 187 | return true; 188 | } 189 | 190 | private String renderBody(RequestBody requestBody) { 191 | try { 192 | final okio.Buffer buffer = new okio.Buffer(); 193 | requestBody.writeTo(buffer); 194 | return buffer.readUtf8(); 195 | } catch (IOException ioe) { 196 | logger.warn("renderBody() error writing body contents out", ioe); 197 | return ""; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/internal/Constants.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.internal; 2 | 3 | public interface Constants { 4 | String HIT_SCREENVIEW = "screenview"; 5 | String HIT_PAGEVIEW = "pageview"; 6 | String HIT_EVENT = "event"; 7 | String HIT_ITEM = "item"; 8 | String HIT_TXN = "transaction"; 9 | String HIT_SOCIAL = "social"; 10 | String HIT_TIMING = "timing"; 11 | String HIT_EXCEPTION = "exception"; 12 | 13 | String TYPE_INTEGER = "integer"; 14 | String TYPE_TEXT = "text"; 15 | String TYPE_BOOLEAN = "boolean"; 16 | String TYPE_CURRENCY = "currency"; 17 | 18 | String TEST_TRACKING_ID = "UA-612100-12"; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/internal/GaUtils.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.internal; 2 | 3 | public class GaUtils { 4 | public static boolean isNotEmpty(String value) { 5 | return !isEmpty(value); 6 | } 7 | 8 | public static boolean isEmpty(String value) { 9 | return value == null || value.trim().length() == 0; 10 | } 11 | 12 | public static StringBuilder appendSystemProperty(StringBuilder sb, String property) { 13 | String value = System.getProperty(property); 14 | if (isNotEmpty(value)) { 15 | if (isNotEmpty(sb.toString())) { 16 | sb.append("/"); 17 | } 18 | sb.append(value); 19 | } 20 | 21 | return sb; 22 | } 23 | 24 | @SafeVarargs 25 | public static T firstNotNull(T... values) { 26 | if (values != null) { 27 | for (T value : values) { 28 | if (value != null) { 29 | return value; 30 | } 31 | } 32 | } 33 | 34 | return null; 35 | } 36 | 37 | public static boolean isBlank(String string) { 38 | return string == null || string.trim().length() == 0; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/internal/GoogleAnalyticsImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | */ 11 | package com.brsanthu.googleanalytics.internal; 12 | 13 | import static com.brsanthu.googleanalytics.internal.GaUtils.isEmpty; 14 | 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Random; 20 | import java.util.concurrent.CompletableFuture; 21 | import java.util.concurrent.ExecutionException; 22 | import java.util.function.Function; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import com.brsanthu.googleanalytics.GoogleAnalytics; 28 | import com.brsanthu.googleanalytics.GoogleAnalyticsConfig; 29 | import com.brsanthu.googleanalytics.GoogleAnalyticsExecutor; 30 | import com.brsanthu.googleanalytics.GoogleAnalyticsStats; 31 | import com.brsanthu.googleanalytics.httpclient.HttpBatchRequest; 32 | import com.brsanthu.googleanalytics.httpclient.HttpClient; 33 | import com.brsanthu.googleanalytics.httpclient.HttpRequest; 34 | import com.brsanthu.googleanalytics.httpclient.HttpResponse; 35 | import com.brsanthu.googleanalytics.request.DefaultRequest; 36 | import com.brsanthu.googleanalytics.request.EventHit; 37 | import com.brsanthu.googleanalytics.request.ExceptionHit; 38 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter; 39 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsRequest; 40 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsResponse; 41 | import com.brsanthu.googleanalytics.request.ItemHit; 42 | import com.brsanthu.googleanalytics.request.PageViewHit; 43 | import com.brsanthu.googleanalytics.request.ScreenViewHit; 44 | import com.brsanthu.googleanalytics.request.SocialHit; 45 | import com.brsanthu.googleanalytics.request.TimingHit; 46 | import com.brsanthu.googleanalytics.request.TransactionHit; 47 | 48 | /** 49 | * This is the main class of this library that accepts the requests from clients 50 | * and sends the events to Google Analytics (GA). 51 | * 52 | * Clients needs to instantiate this object with {@link GoogleAnalyticsConfig} 53 | * and {@link DefaultRequest}. Configuration contains sensible defaults so one 54 | * could just initialize using one of the convenience constructors. 55 | * 56 | * This object is ThreadSafe and it is intended that clients create one instance 57 | * of this for each GA Tracker Id and reuse each time an event needs to be 58 | * posted. 59 | * 60 | * This object contains resources which needs to be shutdown/disposed. So 61 | * {@link #close()} method is called to release all resources. Once close method 62 | * is called, this instance cannot be reused so create new instance if required. 63 | */ 64 | public class GoogleAnalyticsImpl implements GoogleAnalytics, GoogleAnalyticsExecutor { 65 | protected static final Logger logger = LoggerFactory.getLogger(GoogleAnalyticsImpl.class); 66 | 67 | protected final GoogleAnalyticsConfig config; 68 | protected final DefaultRequest defaultRequest; 69 | protected final HttpClient httpClient; 70 | protected boolean inSample = true; 71 | protected GoogleAnalyticsStatsImpl stats = new GoogleAnalyticsStatsImpl(); 72 | protected List currentBatch = new ArrayList<>(); 73 | 74 | public GoogleAnalyticsImpl(GoogleAnalyticsConfig config, DefaultRequest defaultRequest, HttpClient httpClient) { 75 | this.config = config; 76 | this.defaultRequest = defaultRequest; 77 | this.httpClient = httpClient; 78 | performSamplingElection(); 79 | } 80 | 81 | public boolean performSamplingElection() { 82 | // As we are constructed, determine if we should be in the sample or not 83 | int randomNum = new Random().nextInt(100) + 1; 84 | if (randomNum > config.getSamplePercentage()) { 85 | logger.info("Not participating in analytics sample (sample percentage vs random: " 86 | + config.getSamplePercentage() + " " + randomNum + ")"); 87 | inSample = false; 88 | } else { 89 | logger.info("Participating in analytics sample (sample percentage vs random: " 90 | + config.getSamplePercentage() + " " + randomNum + ")"); 91 | inSample = true; 92 | } 93 | return inSample(); 94 | } 95 | 96 | @Override 97 | public boolean inSample() { 98 | return inSample; 99 | } 100 | 101 | @Override 102 | public GoogleAnalyticsConfig getConfig() { 103 | return config; 104 | } 105 | 106 | public DefaultRequest getDefaultRequest() { 107 | return defaultRequest; 108 | } 109 | 110 | @Override 111 | public GoogleAnalyticsResponse post(GoogleAnalyticsRequest request) { 112 | try { 113 | return postAsync(request).get(); 114 | } catch (InterruptedException | ExecutionException e) { 115 | throw new RuntimeException(e); 116 | } 117 | } 118 | 119 | @Override 120 | public CompletableFuture postAsync(GoogleAnalyticsRequest gaReq) { 121 | GoogleAnalyticsResponse response = new GoogleAnalyticsResponse(); 122 | if (!config.isEnabled() || !inSample()) { 123 | return CompletableFuture.completedFuture(response); 124 | } 125 | 126 | if (config.isBatchingEnabled()) { 127 | return CompletableFuture.completedFuture(postBatch(gaReq)); 128 | } else { 129 | return postSingle(gaReq); 130 | } 131 | 132 | } 133 | 134 | protected GoogleAnalyticsResponse postBatch(GoogleAnalyticsRequest gaReq) { 135 | GoogleAnalyticsResponse resp = new GoogleAnalyticsResponse(); 136 | HttpRequest httpReq = createHttpRequest(gaReq); 137 | resp.setRequestParams(httpReq.getBodyParams()); 138 | 139 | if (config.isGatherStats()) { 140 | gatherStats(gaReq); 141 | } 142 | 143 | synchronized (currentBatch) { 144 | currentBatch.add(httpReq); 145 | } 146 | 147 | // If the batch size has reached the configured max, 148 | // then send the batch to google then clear the batch to start a new batch 149 | submitBatch(false); 150 | 151 | return resp; 152 | } 153 | 154 | private void submitBatch(boolean force) { 155 | if (currentBatch.isEmpty()) { 156 | return; 157 | } 158 | 159 | if (isSubmitBatch(force)) { 160 | 161 | // Synchronized block is to ensure only one of the writers will actually write 162 | // the batch. 163 | synchronized (currentBatch) { 164 | 165 | // If two threads pass the if condition and then one of them actually writes, 166 | // other will do the same since they were blocked sync block. this ensures that 167 | // others will not post it even if multiple threads were to wait at sync block 168 | // at same time 169 | // https://en.wikipedia.org/wiki/Double-checked_locking 170 | if (isSubmitBatch(force)) { 171 | logger.debug("Submitting a batch of " + currentBatch.size() + " requests to GA"); 172 | httpClient.postBatch(new HttpBatchRequest().setUrl(config.getBatchUrl()).setRequests(currentBatch)); 173 | currentBatch.clear(); 174 | } 175 | } 176 | } 177 | } 178 | 179 | private boolean isSubmitBatch(boolean force) { 180 | return force || currentBatch.size() >= config.getBatchSize(); 181 | } 182 | 183 | protected CompletableFuture postSingle(GoogleAnalyticsRequest gaReq) { 184 | 185 | HttpRequest httpReq = createHttpRequest(gaReq); 186 | return httpClient.post(httpReq).thenApply(new Function() { 187 | @Override 188 | public GoogleAnalyticsResponse apply(HttpResponse httpResp) { 189 | GoogleAnalyticsResponse response = new GoogleAnalyticsResponse(); 190 | response.setStatusCode(httpResp.getStatusCode()); 191 | response.setRequestParams(httpReq.getBodyParams()); 192 | 193 | if (config.isGatherStats()) { 194 | GoogleAnalyticsImpl.this.gatherStats(gaReq); 195 | } 196 | ; 197 | 198 | return response; 199 | } 200 | }); 201 | } 202 | 203 | private HttpRequest createHttpRequest(GoogleAnalyticsRequest gaReq) { 204 | HttpRequest httpReq = new HttpRequest(config.getUrl()); 205 | 206 | // Process the parameters 207 | processParameters(gaReq, httpReq); 208 | 209 | // Process custom dimensions 210 | processCustomDimensionParameters(gaReq, httpReq); 211 | 212 | // Process custom metrics 213 | processCustomMetricParameters(gaReq, httpReq); 214 | 215 | return httpReq; 216 | } 217 | 218 | protected void processParameters(GoogleAnalyticsRequest request, HttpRequest req) { 219 | 220 | Map requestParms = request.getParameters(); 221 | Map defaultParms = defaultRequest.getParameters(); 222 | 223 | for (GoogleAnalyticsParameter parm : defaultParms.keySet()) { 224 | 225 | String value = requestParms.get(parm); 226 | String defaultValue = defaultParms.get(parm); 227 | 228 | if (isEmpty(value) && !isEmpty(defaultValue)) { 229 | requestParms.put(parm, defaultValue); 230 | } 231 | } 232 | 233 | for (GoogleAnalyticsParameter key : requestParms.keySet()) { 234 | req.addBodyParam(key.getParameterName(), requestParms.get(key)); 235 | } 236 | } 237 | 238 | /** 239 | * Processes the custom dimensions and adds the values to list of parameters, 240 | * which would be posted to GA. 241 | * 242 | * @param request 243 | * @param req 244 | */ 245 | protected void processCustomDimensionParameters(GoogleAnalyticsRequest request, HttpRequest req) { 246 | Map customDimParms = new HashMap(); 247 | for (String defaultCustomDimKey : defaultRequest.customDimensions().keySet()) { 248 | customDimParms.put(defaultCustomDimKey, defaultRequest.customDimensions().get(defaultCustomDimKey)); 249 | } 250 | 251 | Map requestCustomDims = request.customDimensions(); 252 | for (String requestCustomDimKey : requestCustomDims.keySet()) { 253 | customDimParms.put(requestCustomDimKey, requestCustomDims.get(requestCustomDimKey)); 254 | } 255 | 256 | for (String key : customDimParms.keySet()) { 257 | req.addBodyParam(key, customDimParms.get(key)); 258 | } 259 | } 260 | 261 | /** 262 | * Processes the custom metrics and adds the values to list of parameters, which 263 | * would be posted to GA. 264 | * 265 | * @param request 266 | * @param req 267 | */ 268 | protected void processCustomMetricParameters(GoogleAnalyticsRequest request, HttpRequest req) { 269 | Map customMetricParms = new HashMap(); 270 | for (String defaultCustomMetricKey : defaultRequest.custommMetrics().keySet()) { 271 | customMetricParms.put(defaultCustomMetricKey, defaultRequest.custommMetrics().get(defaultCustomMetricKey)); 272 | } 273 | 274 | Map requestCustomMetrics = request.custommMetrics(); 275 | for (String requestCustomDimKey : requestCustomMetrics.keySet()) { 276 | customMetricParms.put(requestCustomDimKey, requestCustomMetrics.get(requestCustomDimKey)); 277 | } 278 | 279 | for (String key : customMetricParms.keySet()) { 280 | req.addBodyParam(key, customMetricParms.get(key)); 281 | } 282 | } 283 | 284 | protected void gatherStats(GoogleAnalyticsRequest request) { 285 | String hitType = request.hitType(); 286 | 287 | if (Constants.HIT_PAGEVIEW.equalsIgnoreCase(hitType)) { 288 | stats.pageViewHit(); 289 | 290 | } else if (Constants.HIT_SCREENVIEW.equals(hitType)) { 291 | stats.screenViewHit(); 292 | 293 | } else if (Constants.HIT_EVENT.equals(hitType)) { 294 | stats.eventHit(); 295 | 296 | } else if (Constants.HIT_ITEM.equals(hitType)) { 297 | stats.itemHit(); 298 | 299 | } else if (Constants.HIT_TXN.equals(hitType)) { 300 | stats.transactionHit(); 301 | 302 | } else if (Constants.HIT_SOCIAL.equals(hitType)) { 303 | stats.socialHit(); 304 | 305 | } else if (Constants.HIT_TIMING.equals(hitType)) { 306 | stats.timingHit(); 307 | 308 | } else if (Constants.HIT_EXCEPTION.equals(hitType)) { 309 | stats.exceptionHit(); 310 | } 311 | } 312 | 313 | @Override 314 | public void close() { 315 | flush(); 316 | 317 | try { 318 | httpClient.close(); 319 | } catch (Exception e) { 320 | // ignore 321 | } 322 | } 323 | 324 | @Override 325 | public GoogleAnalyticsStats getStats() { 326 | return stats; 327 | } 328 | 329 | @Override 330 | public void resetStats() { 331 | stats = new GoogleAnalyticsStatsImpl(); 332 | } 333 | 334 | @Override 335 | public EventHit event() { 336 | return (EventHit) new EventHit().setExecutor(this); 337 | } 338 | 339 | @Override 340 | public ExceptionHit exception() { 341 | return (ExceptionHit) new ExceptionHit().setExecutor(this); 342 | } 343 | 344 | @Override 345 | public ItemHit item() { 346 | return (ItemHit) new ItemHit().setExecutor(this); 347 | } 348 | 349 | @Override 350 | public PageViewHit pageView() { 351 | return (PageViewHit) new PageViewHit().setExecutor(this); 352 | } 353 | 354 | @Override 355 | public PageViewHit pageView(String url, String title) { 356 | return pageView().documentUrl(url).documentTitle(title); 357 | } 358 | 359 | @Override 360 | public PageViewHit pageView(String url, String title, String description) { 361 | return pageView(url, title).contentDescription(description); 362 | } 363 | 364 | @Override 365 | public ScreenViewHit screenView() { 366 | return (ScreenViewHit) new ScreenViewHit().setExecutor(this); 367 | } 368 | 369 | @Override 370 | public ScreenViewHit screenView(String appName, String screenName) { 371 | return screenView().applicationName(appName).screenName(screenName); 372 | } 373 | 374 | @Override 375 | public SocialHit social() { 376 | return (SocialHit) new SocialHit().setExecutor(this); 377 | } 378 | 379 | @Override 380 | public SocialHit social(String socialNetwork, String socialAction, String socialTarget) { 381 | return social().socialNetwork(socialNetwork).socialAction(socialAction).socialActionTarget(socialTarget); 382 | } 383 | 384 | @Override 385 | public TimingHit timing() { 386 | return (TimingHit) new TimingHit().setExecutor(this); 387 | } 388 | 389 | @Override 390 | public TransactionHit transaction() { 391 | return (TransactionHit) new TransactionHit().setExecutor(this); 392 | } 393 | 394 | @Override 395 | public void ifEnabled(Runnable runnable) { 396 | if (!config.isEnabled()) { 397 | return; 398 | } 399 | 400 | runnable.run(); 401 | } 402 | 403 | @Override 404 | public void flush() { 405 | submitBatch(true); 406 | } 407 | 408 | } 409 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/internal/GoogleAnalyticsStatsImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package com.brsanthu.googleanalytics.internal; 16 | 17 | import java.util.concurrent.atomic.AtomicLong; 18 | 19 | import com.brsanthu.googleanalytics.GoogleAnalyticsStats; 20 | 21 | /** 22 | * Collects the basic stats about successful events that have been posted to GA. 23 | * 24 | * @author Santhosh Kumar 25 | */ 26 | public class GoogleAnalyticsStatsImpl implements GoogleAnalyticsStats { 27 | private AtomicLong pageViewHits = new AtomicLong(); 28 | private AtomicLong eventHits = new AtomicLong(); 29 | private AtomicLong screenViewHits = new AtomicLong(); 30 | private AtomicLong itemHits = new AtomicLong(); 31 | private AtomicLong transactionHits = new AtomicLong(); 32 | private AtomicLong timingHits = new AtomicLong(); 33 | private AtomicLong socialHits = new AtomicLong(); 34 | private AtomicLong exceptionHits = new AtomicLong(); 35 | 36 | public void exceptionHit() { 37 | exceptionHits.incrementAndGet(); 38 | } 39 | 40 | public void pageViewHit() { 41 | pageViewHits.incrementAndGet(); 42 | } 43 | 44 | public void eventHit() { 45 | eventHits.incrementAndGet(); 46 | } 47 | 48 | public void screenViewHit() { 49 | screenViewHits.incrementAndGet(); 50 | } 51 | 52 | public void itemHit() { 53 | itemHits.incrementAndGet(); 54 | } 55 | 56 | public void transactionHit() { 57 | transactionHits.incrementAndGet(); 58 | } 59 | 60 | public void socialHit() { 61 | socialHits.incrementAndGet(); 62 | } 63 | 64 | public void timingHit() { 65 | timingHits.incrementAndGet(); 66 | } 67 | 68 | @Override 69 | public long getPageViewHits() { 70 | return pageViewHits.get(); 71 | } 72 | 73 | @Override 74 | public long getEventHits() { 75 | return eventHits.get(); 76 | } 77 | 78 | @Override 79 | public long getScreenViewHits() { 80 | return screenViewHits.get(); 81 | } 82 | 83 | @Override 84 | public long getItemHits() { 85 | return itemHits.get(); 86 | } 87 | 88 | @Override 89 | public long getTransactionHits() { 90 | return transactionHits.get(); 91 | } 92 | 93 | @Override 94 | public long getTimingHits() { 95 | return timingHits.get(); 96 | } 97 | 98 | @Override 99 | public long getSocialHits() { 100 | return socialHits.get(); 101 | } 102 | 103 | @Override 104 | public long getExceptionHits() { 105 | return exceptionHits.get(); 106 | } 107 | 108 | @Override 109 | public String toString() { 110 | return "GoogleAnalyticsStatsImpl [pageViewHits=" + pageViewHits + ", eventHits=" + eventHits + ", screenViewHits=" + screenViewHits 111 | + ", itemHits=" + itemHits + ", transactionHits=" + transactionHits + ", timingHits=" + timingHits + ", socialHits=" + socialHits 112 | + ", exceptionHits=" + exceptionHits + "]"; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/internal/ParameterGetterSetterGenerator.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.internal; 2 | 3 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter; 4 | 5 | public class ParameterGetterSetterGenerator { 6 | 7 | public static void main(String[] args) { 8 | GoogleAnalyticsParameter[] enumConstants = GoogleAnalyticsParameter.class.getEnumConstants(); 9 | for (GoogleAnalyticsParameter parameter : enumConstants) { 10 | String methodName = null;// CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, parameter.toString()); 11 | String constName = parameter.toString(); 12 | 13 | String type = "String"; 14 | 15 | if (parameter.getType().equalsIgnoreCase("integer")) { 16 | type = "Integer"; 17 | 18 | } else if (parameter.getType().equalsIgnoreCase("boolean")) { 19 | type = "Boolean"; 20 | 21 | } else if (parameter.getType().equalsIgnoreCase("currency")) { 22 | type = "Double"; 23 | } 24 | 25 | System.out.println("public T " + methodName + "(" + type + " value) {"); 26 | System.out.println(" set" + type + "(" + constName + ", value);"); 27 | System.out.println(" return (T) this;"); 28 | System.out.println("}"); 29 | 30 | System.out.println("public " + type + " " + methodName + "() {"); 31 | System.out.println(" return get" + type + "(" + constName + ");"); 32 | System.out.println("}"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/logger/DefaultLoggerFactory.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.logger; 2 | 3 | public class DefaultLoggerFactory implements LoggerFactory { 4 | 5 | @Override 6 | public Logger getLogger(Class cls) { 7 | return null; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/logger/Logger.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.logger; 2 | 3 | public interface Logger { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/logger/LoggerFactory.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics.logger; 2 | 3 | public interface LoggerFactory { 4 | Logger getLogger(Class cls); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/EventHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_EVENT; 17 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.EVENT_ACTION; 18 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.EVENT_CATEGORY; 19 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.EVENT_LABEL; 20 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.EVENT_VALUE; 21 | 22 | /** 23 | * GA request to track events. 24 | * 25 | *

26 | * For more information, see 27 | * GA Parameters 28 | * Reference 29 | *

30 | * 31 | * @author Santhosh Kumar 32 | */ 33 | public class EventHit extends GoogleAnalyticsRequest { 34 | 35 | public EventHit() { 36 | this(null, null, null, null); 37 | } 38 | 39 | public EventHit(String eventCategory, String eventAction) { 40 | this(eventCategory, eventAction, null, null); 41 | } 42 | 43 | public EventHit(String eventCategory, String eventAction, String eventLabel, Integer eventValue) { 44 | super(HIT_EVENT); 45 | eventCategory(eventCategory); 46 | eventAction(eventAction); 47 | eventLabel(eventLabel); 48 | eventValue(eventValue); 49 | } 50 | 51 | /** 52 | *

Event Tracking

53 | *

54 | * Optional. 55 | *

56 | *

57 | * Specifies the event category. Must not be empty. 58 | *

59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | * 70 | * 71 | * 72 | * 73 | * 74 | * 75 | * 76 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
ectextNone150 Bytesevent
77 | *
Example value: Category
78 | * Example usage: ec=Category
79 | */ 80 | public EventHit eventCategory(String value) { 81 | setString(EVENT_CATEGORY, value); 82 | return this; 83 | } 84 | 85 | public String eventCategory() { 86 | return getString(EVENT_CATEGORY); 87 | } 88 | 89 | /** 90 | *
91 | *

92 | * Optional. 93 | *

94 | *

95 | * Specifies the event action. Must not be empty. 96 | *

97 | * 98 | * 99 | * 100 | * 101 | * 102 | * 103 | * 104 | * 105 | * 106 | * 107 | * 108 | * 109 | * 110 | * 111 | * 112 | * 113 | * 114 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
eatextNone500 Bytesevent
115 | *
Example value: Action
116 | * Example usage: ea=Action
117 | */ 118 | public EventHit eventAction(String value) { 119 | setString(EVENT_ACTION, value); 120 | return this; 121 | } 122 | 123 | public String eventAction() { 124 | return getString(EVENT_ACTION); 125 | } 126 | 127 | /** 128 | *
129 | *

130 | * Optional. 131 | *

132 | *

133 | * Specifies the event label. 134 | *

135 | * 136 | * 137 | * 138 | * 139 | * 140 | * 141 | * 142 | * 143 | * 144 | * 145 | * 146 | * 147 | * 148 | * 149 | * 150 | * 151 | * 152 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
eltextNone500 Bytesevent
153 | *
Example value: Label
154 | * Example usage: el=Label
155 | */ 156 | public EventHit eventLabel(String value) { 157 | setString(EVENT_LABEL, value); 158 | return this; 159 | } 160 | 161 | public String eventLabel() { 162 | return getString(EVENT_LABEL); 163 | } 164 | 165 | /** 166 | *
167 | *

168 | * Optional. 169 | *

170 | *

171 | * Specifies the event value. Values must be non-negative. 172 | *

173 | * 174 | * 175 | * 176 | * 177 | * 178 | * 179 | * 180 | * 181 | * 182 | * 183 | * 184 | * 185 | * 186 | * 187 | * 188 | * 189 | * 190 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
evintegerNoneNoneevent
191 | *
Example value: 55
192 | * Example usage: ev=55
193 | */ 194 | public EventHit eventValue(Integer value) { 195 | setInteger(EVENT_VALUE, value); 196 | return this; 197 | } 198 | 199 | public Integer eventValue() { 200 | return getInteger(EVENT_VALUE); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/ExceptionHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_EXCEPTION; 17 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.EXCEPTION_DESCRIPTION; 18 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.EXCEPTION_FATAL; 19 | 20 | /** 21 | * GA request to track exceptions. 22 | * 23 | *

24 | * For more information, see 25 | * GA Parameters 26 | * Reference 27 | *

28 | * 29 | * @author Santhosh Kumar 30 | */ 31 | public class ExceptionHit extends GoogleAnalyticsRequest { 32 | 33 | public ExceptionHit() { 34 | this(null); 35 | } 36 | 37 | public ExceptionHit(String exceptionDescription) { 38 | this(exceptionDescription, false); 39 | } 40 | 41 | public ExceptionHit(String exceptionDescription, Boolean fatal) { 42 | super(HIT_EXCEPTION); 43 | exceptionDescription(exceptionDescription); 44 | exceptionFatal(fatal); 45 | } 46 | 47 | /** 48 | *
49 | *

50 | * Optional. 51 | *

52 | *

53 | * Specifies the description of an exception. 54 | *

55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | * 70 | * 71 | * 72 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
exdtextNone150 Bytesexception
73 | *
Example value: DatabaseError
74 | * Example usage: exd=DatabaseError
75 | */ 76 | public ExceptionHit exceptionDescription(String value) { 77 | setString(EXCEPTION_DESCRIPTION, value); 78 | return this; 79 | } 80 | 81 | public String exceptionDescription() { 82 | return getString(EXCEPTION_DESCRIPTION); 83 | } 84 | 85 | /** 86 | *
87 | *

88 | * Optional. 89 | *

90 | *

91 | * Specifies whether the exception was fatal. 92 | *

93 | * 94 | * 95 | * 96 | * 97 | * 98 | * 99 | * 100 | * 101 | * 102 | * 103 | * 104 | * 105 | * 106 | * 107 | * 108 | * 109 | * 110 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
exfboolean1Noneexception
111 | *
Example value: 0
112 | * Example usage: exf=0
113 | */ 114 | public ExceptionHit exceptionFatal(Boolean value) { 115 | setBoolean(EXCEPTION_FATAL, value); 116 | return this; 117 | } 118 | 119 | public Boolean exceptionFatal() { 120 | return getBoolean(EXCEPTION_FATAL); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/GoogleAnalyticsParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License")), 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_EVENT; 17 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_EXCEPTION; 18 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_ITEM; 19 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_SCREENVIEW; 20 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_SOCIAL; 21 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_TIMING; 22 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_TXN; 23 | import static com.brsanthu.googleanalytics.internal.Constants.TYPE_BOOLEAN; 24 | import static com.brsanthu.googleanalytics.internal.Constants.TYPE_CURRENCY; 25 | import static com.brsanthu.googleanalytics.internal.Constants.TYPE_INTEGER; 26 | import static com.brsanthu.googleanalytics.internal.Constants.TYPE_TEXT; 27 | 28 | /** 29 | * Google Analytics Measurement Protocol Parameters. 30 | * 31 | *

32 | * For more information, see 33 | * GA Parameters 34 | * Reference 35 | *

36 | * 37 | * @author Santhosh Kumar 38 | */ 39 | public enum GoogleAnalyticsParameter { 40 | // General 41 | PROTOCOL_VERSION("v", true), 42 | TRACKING_ID("tid", true), 43 | ANONYMIZE_IP("aip", TYPE_BOOLEAN), 44 | QUEUE_TIME("qt", TYPE_INTEGER), 45 | CACHE_BUSTER("z"), 46 | DATA_SOURCE("ds"), 47 | 48 | // Visitor 49 | CLIENT_ID("cid", true), 50 | USER_ID("uid"), 51 | 52 | // Session 53 | SESSION_CONTROL("sc"), 54 | USER_IP("uip"), 55 | USER_AGENT("ua"), 56 | 57 | // geoid 58 | GEOID("geoid"), 59 | 60 | // Traffic Sources 61 | DOCUMENT_REFERRER("dr", 2048), 62 | CAMPAIGN_NAME("cn", 100), 63 | CAMPAIGN_SOURCE("cs", 100), 64 | CAMPAIGN_MEDIUM("cm", 50), 65 | CAMPAIGN_KEYWORD("ck", 500), 66 | CAMPAIGN_CONTENT("cc", 500), 67 | CAMPAIGN_ID("ci", 100), 68 | ADWORDS_ID("gclid"), 69 | DISPLAY_ADS_ID("dclid"), 70 | 71 | // System Info 72 | SCREEN_RESOLUTION("sr", 20), 73 | VIEWPORT_SIZE("vp", 20), 74 | DOCUMENT_ENCODING("de", 20), 75 | SCREEN_COLORS("sd", 20), 76 | USER_LANGUAGE("ul", 20), 77 | JAVA_ENABLED("je", TYPE_BOOLEAN), 78 | FLASH_VERSION("fl", 20), 79 | 80 | // Hit 81 | HIT_TYPE("t", true), 82 | NON_INTERACTION_HIT("ni"), 83 | 84 | // Content Information 85 | DOCUMENT_URL("dl", 2048), 86 | DOCUMENT_HOST_NAME("dh", 100), 87 | DOCUMENT_PATH("dp", 2048), 88 | DOCUMENT_TITLE("dt", 1500), 89 | 90 | CONTENT_DESCRIPTION("cd"), 91 | 92 | LINK_ID("linkid"), 93 | 94 | // App Tracking 95 | APPLICATION_NAME("an", 100), 96 | APPLICATION_ID("aid", 150), 97 | APPLICATION_VERSION("av", 100), 98 | APPLICATION_INSTALLER_ID("aiid", 150), 99 | 100 | // Event Tracking 101 | EVENT_CATEGORY("ec", new String[] { HIT_EVENT }, 150), 102 | EVENT_ACTION("ea", new String[] { HIT_EVENT }, 500), 103 | EVENT_LABEL("el", new String[] { HIT_EVENT }, 500), 104 | EVENT_VALUE("ev", false, TYPE_INTEGER, new String[] { HIT_EVENT }), 105 | 106 | // E-Commerce 107 | TRANSACTION_ID("ti", new String[] { HIT_TXN, HIT_ITEM }, 500), 108 | TRANSACTION_AFFILIATION("ta", new String[] { HIT_TXN }, 500), 109 | TRANSACTION_REVENUE("tr", false, TYPE_CURRENCY, new String[] { HIT_TXN }), 110 | TRANSACTION_SHIPPING("ts", false, TYPE_CURRENCY, new String[] { HIT_TXN }), 111 | TRANSACTION_TAX("tt", false, TYPE_CURRENCY, new String[] { HIT_TXN }), 112 | ITEM_NAME("in", new String[] { HIT_ITEM }, 500), 113 | ITEM_PRICE("ip", false, TYPE_CURRENCY, new String[] { HIT_ITEM }), 114 | ITEM_QUANTITY("iq", false, TYPE_INTEGER, new String[] { HIT_ITEM }), 115 | ITEM_CODE("ic", new String[] { HIT_ITEM }, 500), 116 | ITEM_CATEGORY("iv", new String[] { HIT_ITEM }, 500), 117 | CURRENCY_CODE("cu", new String[] { HIT_TXN, HIT_ITEM }, 10), 118 | 119 | // Social Interactions 120 | SOCIAL_NETWORK("sn", new String[] { HIT_SOCIAL }, 50), 121 | SOCIAL_ACTION("sa", new String[] { HIT_SOCIAL }, 50), 122 | SOCIAL_ACTION_TARGET("st", new String[] { HIT_SOCIAL }, 2048), 123 | 124 | // Timing 125 | USER_TIMING_CATEGORY("utc", new String[] { HIT_TIMING }, 150), 126 | USER_TIMING_VARIABLE_NAME("utv", new String[] { HIT_TIMING }, 500), 127 | USER_TIMING_TIME("utt", false, TYPE_INTEGER, new String[] { HIT_TIMING }), 128 | USER_TIMING_LABEL("utl", new String[] { HIT_TIMING }, 500), 129 | PAGE_LOAD_TIME("plt", false, TYPE_INTEGER, new String[] { HIT_TIMING }), 130 | DNS_TIME("dns", false, TYPE_INTEGER, new String[] { HIT_TIMING }), 131 | PAGE_DOWNLOAD_TIME("pdt", false, TYPE_INTEGER, new String[] { HIT_TIMING }), 132 | REDIRECT_RESPONSE_TIME("rrt", false, TYPE_INTEGER, new String[] { HIT_TIMING }), 133 | TCP_CONNECT_TIME("tcp", false, TYPE_INTEGER, new String[] { HIT_TIMING }), 134 | SERVER_RESPONSE_TIME("srt", false, TYPE_INTEGER, new String[] { HIT_TIMING }), 135 | 136 | // Exceptions 137 | EXCEPTION_DESCRIPTION("exd", new String[] { HIT_EXCEPTION }, 150), 138 | EXCEPTION_FATAL("exf", false, TYPE_BOOLEAN, new String[] { HIT_EXCEPTION }), 139 | 140 | // Experiment Variations 141 | EXPERIMENT_ID("xid", 40), 142 | EXPERIMENT_VARIANT("xvar"), 143 | 144 | // Screen view parameters 145 | SCREEN_NAME("cd", true, TYPE_TEXT, new String[] { HIT_SCREENVIEW }, 2048); 146 | 147 | private String parameterName = null; 148 | private boolean required = false; 149 | private String type = TYPE_TEXT; 150 | private String[] supportedHitTypes = null; 151 | private int maxLength = 0; 152 | 153 | private GoogleAnalyticsParameter(String name) { 154 | this(name, false); 155 | } 156 | 157 | private GoogleAnalyticsParameter(String name, int maxLength) { 158 | this(name, false, null, null, maxLength); 159 | } 160 | 161 | private GoogleAnalyticsParameter(String name, boolean required) { 162 | this(name, required, TYPE_TEXT, null, 0); 163 | } 164 | 165 | private GoogleAnalyticsParameter(String name, String type) { 166 | this(name, false, type, null, 0); 167 | } 168 | 169 | private GoogleAnalyticsParameter(String name, String[] supportedHitTypes) { 170 | this(name, false, TYPE_TEXT, supportedHitTypes, 0); 171 | } 172 | 173 | private GoogleAnalyticsParameter(String name, String[] supportedHitTypes, int maxLength) { 174 | this(name, false, TYPE_TEXT, supportedHitTypes, maxLength); 175 | } 176 | 177 | private GoogleAnalyticsParameter(String name, boolean required, String type, String[] supportedHitTypes) { 178 | this(name, required, type, supportedHitTypes, 0); 179 | } 180 | 181 | private GoogleAnalyticsParameter(String name, boolean required, String type, String[] supportedHitTypes, int maxLength) { 182 | this.parameterName = name; 183 | this.required = required; 184 | if (type == null) { 185 | type = TYPE_TEXT; 186 | } 187 | this.type = type; 188 | this.supportedHitTypes = supportedHitTypes; 189 | this.maxLength = maxLength; 190 | } 191 | 192 | public String getParameterName() { 193 | return parameterName; 194 | } 195 | 196 | public String[] getSupportedHitTypes() { 197 | return supportedHitTypes; 198 | } 199 | 200 | public String getType() { 201 | return type; 202 | } 203 | 204 | public boolean isRequired() { 205 | return required; 206 | } 207 | 208 | public int getMaxLength() { 209 | return maxLength; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/GoogleAnalyticsResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import java.util.Map; 17 | 18 | /** 19 | * Response for GA tracking request. 20 | * 21 | * @author Santhosh Kumar 22 | */ 23 | public class GoogleAnalyticsResponse { 24 | private int statusCode = 200; 25 | private Map requestParams = null; 26 | 27 | public Map getRequestParams() { 28 | return requestParams; 29 | } 30 | 31 | public void setRequestParams(Map postedParms) { 32 | this.requestParams = postedParms; 33 | } 34 | 35 | public void setStatusCode(int statusCode) { 36 | this.statusCode = statusCode; 37 | } 38 | 39 | public int getStatusCode() { 40 | return statusCode; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | StringBuilder builder = new StringBuilder(); 46 | builder.append("Response [statusCode="); 47 | builder.append(statusCode); 48 | builder.append("]"); 49 | return builder.toString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/ItemHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_ITEM; 17 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.CURRENCY_CODE; 18 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.ITEM_CATEGORY; 19 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.ITEM_CODE; 20 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.ITEM_NAME; 21 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.ITEM_PRICE; 22 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.ITEM_QUANTITY; 23 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.TRANSACTION_ID; 24 | 25 | /** 26 | * GA request to track items as part of ecommerce transaction. 27 | * 28 | *

29 | * For more information, see 30 | * GA Parameters 31 | * Reference 32 | *

33 | * 34 | * @author Santhosh Kumar 35 | */ 36 | public class ItemHit extends GoogleAnalyticsRequest { 37 | 38 | public ItemHit() { 39 | super(HIT_ITEM); 40 | } 41 | 42 | /** 43 | *
44 | *

45 | * Required for transaction hit type.
46 | * Required for item hit type. 47 | *

48 | *

49 | * A unique identifier for the transaction. This value should be the same for both the Transaction hit and Items 50 | * hits associated to the particular transaction. 51 | *

52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
titextNone500 Bytestransaction, item
70 | *
Example value: OD564
71 | * Example usage: ti=OD564
72 | */ 73 | public ItemHit txId(String value) { 74 | setString(TRANSACTION_ID, value); 75 | return this; 76 | } 77 | 78 | public String txId() { 79 | return getString(TRANSACTION_ID); 80 | } 81 | 82 | /** 83 | *
84 | *

85 | * Required for item hit type. 86 | *

87 | *

88 | * Specifies the item name. 89 | *

90 | * 91 | * 92 | * 93 | * 94 | * 95 | * 96 | * 97 | * 98 | * 99 | * 100 | * 101 | * 102 | * 103 | * 104 | * 105 | * 106 | * 107 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
intextNone500 Bytesitem
108 | *
Example value: Shoe
109 | * Example usage: in=Shoe
110 | */ 111 | public ItemHit itemName(String value) { 112 | setString(ITEM_NAME, value); 113 | return this; 114 | } 115 | 116 | public String itemName() { 117 | return getString(ITEM_NAME); 118 | } 119 | 120 | /** 121 | *
122 | *

123 | * Optional. 124 | *

125 | *

126 | * Specifies the price for a single item / unit. 127 | *

128 | * 129 | * 130 | * 131 | * 132 | * 133 | * 134 | * 135 | * 136 | * 137 | * 138 | * 139 | * 140 | * 141 | * 142 | * 143 | * 144 | * 145 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
ipcurrency0Noneitem
146 | *
Example value: 3.50
147 | * Example usage: ip=3.50
148 | */ 149 | public ItemHit itemPrice(Double value) { 150 | setDouble(ITEM_PRICE, value); 151 | return this; 152 | } 153 | 154 | public Double itemPrice() { 155 | return getDouble(ITEM_PRICE); 156 | } 157 | 158 | /** 159 | *
160 | *

161 | * Optional. 162 | *

163 | *

164 | * Specifies the number of items purchased. 165 | *

166 | * 167 | * 168 | * 169 | * 170 | * 171 | * 172 | * 173 | * 174 | * 175 | * 176 | * 177 | * 178 | * 179 | * 180 | * 181 | * 182 | * 183 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
iqinteger0Noneitem
184 | *
Example value: 4
185 | * Example usage: iq=4
186 | */ 187 | public ItemHit itemQuantity(Integer value) { 188 | setInteger(ITEM_QUANTITY, value); 189 | return this; 190 | } 191 | 192 | public Integer itemQuantity() { 193 | return getInteger(ITEM_QUANTITY); 194 | } 195 | 196 | /** 197 | *
198 | *

199 | * Optional. 200 | *

201 | *

202 | * Specifies the SKU or item code. 203 | *

204 | * 205 | * 206 | * 207 | * 208 | * 209 | * 210 | * 211 | * 212 | * 213 | * 214 | * 215 | * 216 | * 217 | * 218 | * 219 | * 220 | * 221 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
ictextNone500 Bytesitem
222 | *
Example value: SKU47
223 | * Example usage: ic=SKU47
224 | */ 225 | public ItemHit itemCode(String value) { 226 | setString(ITEM_CODE, value); 227 | return this; 228 | } 229 | 230 | public String itemCode() { 231 | return getString(ITEM_CODE); 232 | } 233 | 234 | /** 235 | *
236 | *

237 | * Optional. 238 | *

239 | *

240 | * Specifies the category that the item belongs to. 241 | *

242 | * 243 | * 244 | * 245 | * 246 | * 247 | * 248 | * 249 | * 250 | * 251 | * 252 | * 253 | * 254 | * 255 | * 256 | * 257 | * 258 | * 259 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
ivtextNone500 Bytesitem
260 | *
Example value: Blue
261 | * Example usage: iv=Blue
262 | */ 263 | public ItemHit itemCategory(String value) { 264 | setString(ITEM_CATEGORY, value); 265 | return this; 266 | } 267 | 268 | public String itemCategory() { 269 | return getString(ITEM_CATEGORY); 270 | } 271 | 272 | /** 273 | *
274 | *

275 | * Optional. 276 | *

277 | *

278 | * When present indicates the local currency for all transaction currency values. Value should be a valid ISO 4217 279 | * currency code. 280 | *

281 | * 282 | * 283 | * 284 | * 285 | * 286 | * 287 | * 288 | * 289 | * 290 | * 291 | * 292 | * 293 | * 294 | * 295 | * 296 | * 297 | * 298 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
cutextNone10 Bytestransaction, item
299 | *
Example value: EUR
300 | * Example usage: cu=EUR
301 | */ 302 | public ItemHit currencyCode(String value) { 303 | setString(CURRENCY_CODE, value); 304 | return this; 305 | } 306 | 307 | public String currencyCode() { 308 | return getString(CURRENCY_CODE); 309 | } 310 | 311 | } 312 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/PageViewHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_PAGEVIEW; 17 | 18 | /** 19 | * GA request to track a typical web page view 20 | * 21 | *

22 | * For more information, see 23 | * GA Parameters 24 | * Reference 25 | *

26 | * 27 | * @author Santhosh Kumar 28 | */ 29 | public class PageViewHit extends GoogleAnalyticsRequest { 30 | public PageViewHit() { 31 | this(null, null, null); 32 | } 33 | 34 | public PageViewHit(String url, String title) { 35 | this(url, title, null); 36 | } 37 | 38 | public PageViewHit(String url, String title, String description) { 39 | super(HIT_PAGEVIEW); 40 | documentUrl(url); 41 | documentTitle(title); 42 | contentDescription(description); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/ScreenViewHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_SCREENVIEW; 17 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.SCREEN_NAME; 18 | 19 | /** 20 | * GA request to track a typical web page view 21 | * 22 | *

23 | * For more information, see 24 | * GA Parameters 25 | * Reference 26 | *

27 | * 28 | * @author Santhosh Kumar 29 | */ 30 | public class ScreenViewHit extends GoogleAnalyticsRequest { 31 | public ScreenViewHit() { 32 | this(null, null); 33 | } 34 | 35 | public ScreenViewHit(String appName, String screenName) { 36 | super(HIT_SCREENVIEW); 37 | screenName(screenName); 38 | applicationName(appName); 39 | } 40 | 41 | public ScreenViewHit screenName(String value) { 42 | setString(SCREEN_NAME, value); 43 | return this; 44 | } 45 | 46 | public String screenName() { 47 | return getString(SCREEN_NAME); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/SocialHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_SOCIAL; 17 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.SOCIAL_ACTION; 18 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.SOCIAL_ACTION_TARGET; 19 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.SOCIAL_NETWORK; 20 | 21 | /** 22 | * GA request to track social interactions 23 | * 24 | *

25 | * For more information, see 26 | * GA Parameters 27 | * Reference 28 | *

29 | * 30 | * @author Santhosh Kumar 31 | */ 32 | public class SocialHit extends GoogleAnalyticsRequest { 33 | 34 | public SocialHit() { 35 | this(null, null, null); 36 | } 37 | 38 | public SocialHit(String socialNetwork, String socialAction, String socialTarget) { 39 | super(HIT_SOCIAL); 40 | socialAction(socialAction); 41 | socialNetwork(socialNetwork); 42 | socialActionTarget(socialTarget); 43 | } 44 | 45 | /** 46 | *
47 | *

48 | * Required for social hit type. 49 | *

50 | *

51 | * Specifies the social network, for example Facebook or Google Plus. 52 | *

53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | * 70 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
sntextNone50 Bytessocial
71 | *
Example value: facebook
72 | * Example usage: sn=facebook
73 | */ 74 | public SocialHit socialNetwork(String value) { 75 | setString(SOCIAL_NETWORK, value); 76 | return this; 77 | } 78 | 79 | public String socialNetwork() { 80 | return getString(SOCIAL_NETWORK); 81 | } 82 | 83 | /** 84 | *
85 | *

86 | * Required for social hit type. 87 | *

88 | *

89 | * Specifies the social interaction action. For example on Google Plus when a user clicks the +1 button, the social 90 | * action is 'plus'. 91 | *

92 | * 93 | * 94 | * 95 | * 96 | * 97 | * 98 | * 99 | * 100 | * 101 | * 102 | * 103 | * 104 | * 105 | * 106 | * 107 | * 108 | * 109 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
satextNone50 Bytessocial
110 | *
Example value: like
111 | * Example usage: sa=like
112 | */ 113 | public SocialHit socialAction(String value) { 114 | setString(SOCIAL_ACTION, value); 115 | return this; 116 | } 117 | 118 | public String socialAction() { 119 | return getString(SOCIAL_ACTION); 120 | } 121 | 122 | /** 123 | *
124 | *

125 | * Required for social hit type. 126 | *

127 | *

128 | * Specifies the target of a social interaction. This value is typically a URL but can be any text. 129 | *

130 | * 131 | * 132 | * 133 | * 134 | * 135 | * 136 | * 137 | * 138 | * 139 | * 140 | * 141 | * 142 | * 143 | * 144 | * 145 | * 146 | * 147 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
sttextNone2048 Bytessocial
148 | *
Example value: http://foo.com
149 | * Example usage: st=http%3A%2F%2Ffoo.com
150 | */ 151 | public SocialHit socialActionTarget(String value) { 152 | setString(SOCIAL_ACTION_TARGET, value); 153 | return this; 154 | } 155 | 156 | public String socialActionTarget() { 157 | return getString(SOCIAL_ACTION_TARGET); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/TimingHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_TIMING; 17 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.DNS_TIME; 18 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.PAGE_DOWNLOAD_TIME; 19 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.PAGE_LOAD_TIME; 20 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.REDIRECT_RESPONSE_TIME; 21 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.SERVER_RESPONSE_TIME; 22 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.TCP_CONNECT_TIME; 23 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.USER_TIMING_CATEGORY; 24 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.USER_TIMING_LABEL; 25 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.USER_TIMING_TIME; 26 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.USER_TIMING_VARIABLE_NAME; 27 | 28 | /** 29 | * GA request to track performance timings like page load time, server response time etc. 30 | * 31 | *

32 | * For more information, see 33 | * GA Parameters 34 | * Reference 35 | *

36 | * 37 | * @author Santhosh Kumar 38 | */ 39 | public class TimingHit extends GoogleAnalyticsRequest { 40 | public TimingHit() { 41 | super(HIT_TIMING); 42 | } 43 | 44 | /** 45 | *
46 | *

47 | * Optional. 48 | *

49 | *

50 | * Specifies the user timing category. 51 | *

52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
utctextNone150 Bytestiming
70 | *
Example value: category
71 | * Example usage: utc=category
72 | */ 73 | public TimingHit userTimingCategory(String value) { 74 | setString(USER_TIMING_CATEGORY, value); 75 | return this; 76 | } 77 | 78 | public String userTimingCategory() { 79 | return getString(USER_TIMING_CATEGORY); 80 | } 81 | 82 | /** 83 | *
84 | *

85 | * Optional. 86 | *

87 | *

88 | * Specifies the user timing variable. 89 | *

90 | * 91 | * 92 | * 93 | * 94 | * 95 | * 96 | * 97 | * 98 | * 99 | * 100 | * 101 | * 102 | * 103 | * 104 | * 105 | * 106 | * 107 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
utvtextNone500 Bytestiming
108 | *
Example value: lookup
109 | * Example usage: utv=lookup
110 | */ 111 | public TimingHit userTimingVariableName(String value) { 112 | setString(USER_TIMING_VARIABLE_NAME, value); 113 | return this; 114 | } 115 | 116 | public String userTimingVariableName() { 117 | return getString(USER_TIMING_VARIABLE_NAME); 118 | } 119 | 120 | /** 121 | *
122 | *

123 | * Optional. 124 | *

125 | *

126 | * Specifies the user timing value. The value is in milliseconds. 127 | *

128 | * 129 | * 130 | * 131 | * 132 | * 133 | * 134 | * 135 | * 136 | * 137 | * 138 | * 139 | * 140 | * 141 | * 142 | * 143 | * 144 | * 145 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
uttintegerNoneNonetiming
146 | *
Example value: 123
147 | * Example usage: utt=123
148 | */ 149 | public TimingHit userTimingTime(Integer value) { 150 | setInteger(USER_TIMING_TIME, value); 151 | return this; 152 | } 153 | 154 | public Integer userTimingTime() { 155 | return getInteger(USER_TIMING_TIME); 156 | } 157 | 158 | /** 159 | *
160 | *

161 | * Optional. 162 | *

163 | *

164 | * Specifies the user timing label. 165 | *

166 | * 167 | * 168 | * 169 | * 170 | * 171 | * 172 | * 173 | * 174 | * 175 | * 176 | * 177 | * 178 | * 179 | * 180 | * 181 | * 182 | * 183 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
utltextNone500 Bytestiming
184 | *
Example value: label
185 | * Example usage: utl=label
186 | */ 187 | public TimingHit userTimingLabel(String value) { 188 | setString(USER_TIMING_LABEL, value); 189 | return this; 190 | } 191 | 192 | public String userTimingLabel() { 193 | return getString(USER_TIMING_LABEL); 194 | } 195 | 196 | /** 197 | *
198 | *

199 | * Optional. 200 | *

201 | *

202 | * Specifies the time it took for a page to load. The value is in milliseconds. 203 | *

204 | * 205 | * 206 | * 207 | * 208 | * 209 | * 210 | * 211 | * 212 | * 213 | * 214 | * 215 | * 216 | * 217 | * 218 | * 219 | * 220 | * 221 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
pltintegerNoneNonetiming
222 | *
Example value: 3554
223 | * Example usage: plt=3554
224 | */ 225 | public TimingHit pageLoadTime(Integer value) { 226 | setInteger(PAGE_LOAD_TIME, value); 227 | return this; 228 | } 229 | 230 | public Integer pageLoadTime() { 231 | return getInteger(PAGE_LOAD_TIME); 232 | } 233 | 234 | /** 235 | *
236 | *

237 | * Optional. 238 | *

239 | *

240 | * Specifies the time it took to do a DNS lookup.The value is in milliseconds. 241 | *

242 | * 243 | * 244 | * 245 | * 246 | * 247 | * 248 | * 249 | * 250 | * 251 | * 252 | * 253 | * 254 | * 255 | * 256 | * 257 | * 258 | * 259 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
dnsintegerNoneNonetiming
260 | *
Example value: 43
261 | * Example usage: dns=43
262 | */ 263 | public TimingHit dnsTime(Integer value) { 264 | setInteger(DNS_TIME, value); 265 | return this; 266 | } 267 | 268 | public Integer dnsTime() { 269 | return getInteger(DNS_TIME); 270 | } 271 | 272 | /** 273 | *
274 | *

275 | * Optional. 276 | *

277 | *

278 | * Specifies the time it took for the page to be downloaded. The value is in milliseconds. 279 | *

280 | * 281 | * 282 | * 283 | * 284 | * 285 | * 286 | * 287 | * 288 | * 289 | * 290 | * 291 | * 292 | * 293 | * 294 | * 295 | * 296 | * 297 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
pdtintegerNoneNonetiming
298 | *
Example value: 500
299 | * Example usage: pdt=500
300 | */ 301 | public TimingHit pageDownloadTime(Integer value) { 302 | setInteger(PAGE_DOWNLOAD_TIME, value); 303 | return this; 304 | } 305 | 306 | public Integer pageDownloadTime() { 307 | return getInteger(PAGE_DOWNLOAD_TIME); 308 | } 309 | 310 | /** 311 | *
312 | *

313 | * Optional. 314 | *

315 | *

316 | * Specifies the time it took for any redirects to happen. The value is in milliseconds. 317 | *

318 | * 319 | * 320 | * 321 | * 322 | * 323 | * 324 | * 325 | * 326 | * 327 | * 328 | * 329 | * 330 | * 331 | * 332 | * 333 | * 334 | * 335 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
rrtintegerNoneNonetiming
336 | *
Example value: 500
337 | * Example usage: rrt=500
338 | */ 339 | public TimingHit redirectResponseTime(Integer value) { 340 | setInteger(REDIRECT_RESPONSE_TIME, value); 341 | return this; 342 | } 343 | 344 | public Integer redirectResponseTime() { 345 | return getInteger(REDIRECT_RESPONSE_TIME); 346 | } 347 | 348 | /** 349 | *
350 | *

351 | * Optional. 352 | *

353 | *

354 | * Specifies the time it took for a TCP connection to be made. The value is in milliseconds. 355 | *

356 | * 357 | * 358 | * 359 | * 360 | * 361 | * 362 | * 363 | * 364 | * 365 | * 366 | * 367 | * 368 | * 369 | * 370 | * 371 | * 372 | * 373 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
tcpintegerNoneNonetiming
374 | *
Example value: 500
375 | * Example usage: tcp=500
376 | */ 377 | public TimingHit tcpConnectTime(Integer value) { 378 | setInteger(TCP_CONNECT_TIME, value); 379 | return this; 380 | } 381 | 382 | public Integer tcpConnectTime() { 383 | return getInteger(TCP_CONNECT_TIME); 384 | } 385 | 386 | /** 387 | *
388 | *

389 | * Optional. 390 | *

391 | *

392 | * Specifies the time it took for the server to respond after the connect time. The value is in milliseconds. 393 | *

394 | * 395 | * 396 | * 397 | * 398 | * 399 | * 400 | * 401 | * 402 | * 403 | * 404 | * 405 | * 406 | * 407 | * 408 | * 409 | * 410 | * 411 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
srtintegerNoneNonetiming
412 | *
Example value: 500
413 | * Example usage: srt=500
414 | */ 415 | public TimingHit serverResponseTime(Integer value) { 416 | setInteger(SERVER_RESPONSE_TIME, value); 417 | return this; 418 | } 419 | 420 | public Integer serverResponseTime() { 421 | return getInteger(SERVER_RESPONSE_TIME); 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /src/main/java/com/brsanthu/googleanalytics/request/TransactionHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.brsanthu.googleanalytics.request; 15 | 16 | import static com.brsanthu.googleanalytics.internal.Constants.HIT_TXN; 17 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.CURRENCY_CODE; 18 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.TRANSACTION_AFFILIATION; 19 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.TRANSACTION_ID; 20 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.TRANSACTION_REVENUE; 21 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.TRANSACTION_SHIPPING; 22 | import static com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter.TRANSACTION_TAX; 23 | 24 | /** 25 | * GA request to track ecommerce transaction. 26 | * 27 | *

28 | * For more information, see 29 | * GA Parameters 30 | * Reference 31 | *

32 | * 33 | * @author Santhosh Kumar 34 | */ 35 | public class TransactionHit extends GoogleAnalyticsRequest { 36 | 37 | public TransactionHit() { 38 | this(null); 39 | } 40 | 41 | public TransactionHit(String txId) { 42 | this(txId, null); 43 | } 44 | 45 | public TransactionHit(String txId, Double txRevenue) { 46 | this(txId, null, txRevenue); 47 | } 48 | 49 | public TransactionHit(String txId, String txAffiliation, Double txRevenue) { 50 | this(txId, txAffiliation, txRevenue, null, null, "USD"); 51 | } 52 | 53 | public TransactionHit(String txId, String txAffiliation, Double txRevenue, String currencyCode) { 54 | this(txId, txAffiliation, txRevenue, null, null, currencyCode); 55 | } 56 | 57 | public TransactionHit(String txId, String txAffiliation, Double txRevenue, Double txShipping, Double txTax, String currencyCode) { 58 | super(HIT_TXN); 59 | txId(txId); 60 | txAffiliation(txAffiliation); 61 | txRevenue(txRevenue); 62 | txShipping(txShipping); 63 | txTax(txTax); 64 | currencyCode(currencyCode); 65 | } 66 | 67 | /** 68 | *
69 | *

70 | * Required for transaction hit type.
71 | * Required for item hit type. 72 | *

73 | *

74 | * A unique identifier for the transaction. This value should be the same for both the Transaction hit and Items 75 | * hits associated to the particular transaction. 76 | *

77 | * 78 | * 79 | * 80 | * 81 | * 82 | * 83 | * 84 | * 85 | * 86 | * 87 | * 88 | * 89 | * 90 | * 91 | * 92 | * 93 | * 94 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
titextNone500 Bytestransaction, item
95 | *
Example value: OD564
96 | * Example usage: ti=OD564
97 | */ 98 | public TransactionHit txId(String value) { 99 | setString(TRANSACTION_ID, value); 100 | return this; 101 | } 102 | 103 | public String txId() { 104 | return getString(TRANSACTION_ID); 105 | } 106 | 107 | /** 108 | *
109 | *

110 | * Optional. 111 | *

112 | *

113 | * Specifies the affiliation or store name. 114 | *

115 | * 116 | * 117 | * 118 | * 119 | * 120 | * 121 | * 122 | * 123 | * 124 | * 125 | * 126 | * 127 | * 128 | * 129 | * 130 | * 131 | * 132 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
tatextNone500 Bytestransaction
133 | *
Example value: Member
134 | * Example usage: ta=Member
135 | */ 136 | public TransactionHit txAffiliation(String value) { 137 | setString(TRANSACTION_AFFILIATION, value); 138 | return this; 139 | } 140 | 141 | public String txAffiliation() { 142 | return getString(TRANSACTION_AFFILIATION); 143 | } 144 | 145 | /** 146 | *
147 | *

148 | * Optional. 149 | *

150 | *

151 | * Specifies the total revenue associated with the transaction. This value should include any shipping or tax costs. 152 | *

153 | * 154 | * 155 | * 156 | * 157 | * 158 | * 159 | * 160 | * 161 | * 162 | * 163 | * 164 | * 165 | * 166 | * 167 | * 168 | * 169 | * 170 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
trcurrency0Nonetransaction
171 | *
Example value: 15.47
172 | * Example usage: tr=15.47
173 | */ 174 | public TransactionHit txRevenue(Double value) { 175 | setDouble(TRANSACTION_REVENUE, value); 176 | return this; 177 | } 178 | 179 | public Double txRevenue() { 180 | return getDouble(TRANSACTION_REVENUE); 181 | } 182 | 183 | /** 184 | *
185 | *

186 | * Optional. 187 | *

188 | *

189 | * Specifies the total shipping cost of the transaction. 190 | *

191 | * 192 | * 193 | * 194 | * 195 | * 196 | * 197 | * 198 | * 199 | * 200 | * 201 | * 202 | * 203 | * 204 | * 205 | * 206 | * 207 | * 208 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
tscurrency0Nonetransaction
209 | *
Example value: 3.50
210 | * Example usage: ts=3.50
211 | */ 212 | public TransactionHit txShipping(Double value) { 213 | setDouble(TRANSACTION_SHIPPING, value); 214 | return this; 215 | } 216 | 217 | public Double txShipping() { 218 | return getDouble(TRANSACTION_SHIPPING); 219 | } 220 | 221 | /** 222 | *
223 | *

224 | * Optional. 225 | *

226 | *

227 | * Specifies the total tax of the transaction. 228 | *

229 | * 230 | * 231 | * 232 | * 233 | * 234 | * 235 | * 236 | * 237 | * 238 | * 239 | * 240 | * 241 | * 242 | * 243 | * 244 | * 245 | * 246 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
ttcurrency0Nonetransaction
247 | *
Example value: 11.20
248 | * Example usage: tt=11.20
249 | */ 250 | public TransactionHit txTax(Double value) { 251 | setDouble(TRANSACTION_TAX, value); 252 | return this; 253 | } 254 | 255 | public Double txTax() { 256 | return getDouble(TRANSACTION_TAX); 257 | } 258 | 259 | /** 260 | *
261 | *

262 | * Optional. 263 | *

264 | *

265 | * When present indicates the local currency for all transaction currency values. Value should be a valid ISO 4217 266 | * currency code. 267 | *

268 | * 269 | * 270 | * 271 | * 272 | * 273 | * 274 | * 275 | * 276 | * 277 | * 278 | * 279 | * 280 | * 281 | * 282 | * 283 | * 284 | * 285 | *
ParameterValue TypeDefault ValueMax LengthSupported Hit Types
cutextNone10 Bytestransaction, item
286 | *
Example value: EUR
287 | * Example usage: cu=EUR
288 | */ 289 | public TransactionHit currencyCode(String value) { 290 | setString(CURRENCY_CODE, value); 291 | return this; 292 | } 293 | 294 | public String currencyCode() { 295 | return getString(CURRENCY_CODE); 296 | } 297 | 298 | } 299 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/AwtRequestParameterDiscovererTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertNull; 5 | 6 | import org.junit.Test; 7 | 8 | import com.brsanthu.googleanalytics.discovery.AwtRequestParameterDiscoverer; 9 | import com.brsanthu.googleanalytics.request.DefaultRequest; 10 | 11 | public class AwtRequestParameterDiscovererTest { 12 | 13 | @Test 14 | public void testDiscoverParameters() throws Exception { 15 | AwtRequestParameterDiscoverer discoverer = new AwtRequestParameterDiscoverer(); 16 | DefaultRequest request = new DefaultRequest(); 17 | 18 | GoogleAnalyticsConfig config = new GoogleAnalyticsConfig(); 19 | 20 | assertNull(config.getUserAgent()); 21 | assertNull(request.userLanguage()); 22 | assertNull(request.documentEncoding()); 23 | assertNull(request.screenColors()); 24 | assertNull(request.screenResolution()); 25 | 26 | discoverer.discoverParameters(config, request); 27 | 28 | assertNotNull(config.getUserAgent()); 29 | assertNotNull(request.userLanguage()); 30 | assertNotNull(request.documentEncoding()); 31 | assertNotNull(request.screenColors()); 32 | assertNotNull(request.screenResolution()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/DefaultRequestParameterDiscovererTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertNull; 5 | 6 | import org.junit.Test; 7 | 8 | import com.brsanthu.googleanalytics.discovery.DefaultRequestParameterDiscoverer; 9 | import com.brsanthu.googleanalytics.request.DefaultRequest; 10 | 11 | public class DefaultRequestParameterDiscovererTest { 12 | 13 | @Test 14 | public void testDiscoverParameters() throws Exception { 15 | DefaultRequestParameterDiscoverer discoverer = new DefaultRequestParameterDiscoverer(); 16 | DefaultRequest request = new DefaultRequest(); 17 | 18 | GoogleAnalyticsConfig config = new GoogleAnalyticsConfig(); 19 | 20 | assertNull(config.getUserAgent()); 21 | assertNull(request.userLanguage()); 22 | assertNull(request.documentEncoding()); 23 | 24 | discoverer.discoverParameters(config, request); 25 | 26 | assertNotNull(config.getUserAgent()); 27 | assertNotNull(request.userLanguage()); 28 | assertNotNull(request.documentEncoding()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/EventHitTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import com.brsanthu.googleanalytics.request.EventHit; 8 | 9 | public class EventHitTest { 10 | 11 | @Test 12 | public void testEventHit() throws Exception { 13 | EventHit eventHit = new EventHit("eventCategory", "eventAction", "eventLabel", 10); 14 | assertEquals("eventCategory", eventHit.eventCategory()); 15 | assertEquals("eventAction", eventHit.eventAction()); 16 | assertEquals("eventLabel", eventHit.eventLabel()); 17 | assertEquals(new Integer(10), eventHit.eventValue()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/GaUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | import com.brsanthu.googleanalytics.internal.GaUtils; 8 | 9 | public class GaUtilsTest { 10 | 11 | @Test 12 | public void testIsEmpty() throws Exception { 13 | assertEquals(true, GaUtils.isEmpty(null)); 14 | assertEquals(true, GaUtils.isEmpty("")); 15 | assertEquals(true, GaUtils.isEmpty(" ")); 16 | assertEquals(false, GaUtils.isEmpty("value")); 17 | assertEquals(false, GaUtils.isEmpty(" value ")); 18 | } 19 | 20 | @Test 21 | public void isNotEmpty() throws Exception { 22 | assertEquals(false, GaUtils.isNotEmpty(null)); 23 | assertEquals(false, GaUtils.isNotEmpty("")); 24 | assertEquals(false, GaUtils.isNotEmpty(" ")); 25 | assertEquals(true, GaUtils.isNotEmpty("value")); 26 | assertEquals(true, GaUtils.isNotEmpty(" value ")); 27 | } 28 | 29 | @Test 30 | public void testAppendSystemProperty() throws Exception { 31 | System.setProperty("test", "test"); 32 | assertEquals("", GaUtils.appendSystemProperty(new StringBuilder(), "nonexistent").toString()); 33 | assertEquals("test", GaUtils.appendSystemProperty(new StringBuilder(), "test").toString()); 34 | assertEquals("foo/test", GaUtils.appendSystemProperty(new StringBuilder("foo"), "test").toString()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/GoogleAnalyticsApacheHttpTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static com.brsanthu.googleanalytics.internal.Constants.TEST_TRACKING_ID; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertNotNull; 6 | 7 | import org.junit.After; 8 | import org.junit.Before; 9 | import org.junit.BeforeClass; 10 | import org.junit.Test; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import com.brsanthu.googleanalytics.request.DefaultRequest; 15 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsResponse; 16 | 17 | public class GoogleAnalyticsApacheHttpTest { 18 | private static final Logger logger = LoggerFactory.getLogger(GoogleAnalyticsApacheHttpTest.class); 19 | 20 | protected static GoogleAnalytics ga = null; 21 | 22 | protected long startTime; 23 | protected long callStartTime; 24 | 25 | @BeforeClass 26 | public static void setup() { 27 | ga = new GoogleAnalyticsApacheHttpTest().getTestBuilder().build(); 28 | } 29 | 30 | @Before 31 | public void start() { 32 | startTime = System.currentTimeMillis(); 33 | } 34 | 35 | @After 36 | public void end() { 37 | logger.debug("Test took: " + (System.currentTimeMillis() - startTime)); 38 | } 39 | 40 | protected void startCall() { 41 | callStartTime = System.currentTimeMillis(); 42 | } 43 | 44 | protected void stopCall() { 45 | logger.debug("Call took: " + (System.currentTimeMillis() - callStartTime));; 46 | } 47 | 48 | protected GoogleAnalyticsBuilder getTestBuilder() { 49 | return GoogleAnalytics.builder().withTrackingId(TEST_TRACKING_ID).withAppName("Junit ApacheHttp Test").withAppVersion("1.0.0"); 50 | } 51 | 52 | @Test 53 | public void testPageView() throws Exception { 54 | startCall(); 55 | ga.pageView("http://www.google.com", "Search").send(); 56 | stopCall(); 57 | startCall(); 58 | ga.pageView("http://www.google.com", "Search").sendAsync(); 59 | stopCall(); 60 | } 61 | 62 | @Test 63 | public void testPageViewBatch() throws Exception { 64 | startCall(); 65 | GoogleAnalytics lga = getTestBuilder().withConfig(new GoogleAnalyticsConfig().setBatchingEnabled(true).setBatchSize(4)).build(); 66 | lga.pageView("http://www.google.com", "Search").send(); 67 | lga.pageView("http://www.google.com", "Search2").send(); 68 | lga.pageView("http://www.google.com", "Search3").send(); 69 | lga.pageView("http://www.google.com", "Search4").send(); 70 | lga.pageView("http://www.google.com", "Search5").send(); 71 | lga.flush(); 72 | stopCall(); 73 | } 74 | @Test 75 | public void testSocial() throws Exception { 76 | ga.social("Facebook", "Like", "https://www.google.com").send(); 77 | ga.social("Google+", "Post", "It is a comment").send(); 78 | ga.social("Twitter", "Repost", "Post").send(); 79 | } 80 | 81 | @Test 82 | public void testGatherStats() throws Exception { 83 | ga.getConfig().setGatherStats(false); 84 | ga.resetStats(); 85 | ga.pageView().send(); 86 | ga.pageView().send(); 87 | ga.pageView().send(); 88 | ga.screenView().send(); 89 | ga.screenView().send(); 90 | ga.item().send(); 91 | 92 | assertEquals(0, ga.getStats().getPageViewHits()); 93 | assertEquals(0, ga.getStats().getScreenViewHits()); 94 | assertEquals(0, ga.getStats().getItemHits()); 95 | 96 | ga.getConfig().setGatherStats(true); 97 | ga.resetStats(); 98 | ga.pageView().send(); 99 | ga.pageView().send(); 100 | ga.pageView().send(); 101 | ga.screenView().send(); 102 | ga.screenView().send(); 103 | ga.item().send(); 104 | 105 | assertEquals(3, ga.getStats().getPageViewHits()); 106 | assertEquals(2, ga.getStats().getScreenViewHits()); 107 | assertEquals(1, ga.getStats().getItemHits()); 108 | } 109 | 110 | @Test 111 | public void testCustomDimensions() throws Exception { 112 | DefaultRequest defaultRequest = new DefaultRequest(); 113 | defaultRequest.customDimension(1, "foo"); 114 | defaultRequest.customDimension(5, "bar"); 115 | 116 | // Local ga 117 | GoogleAnalytics lga = getTestBuilder().withDefaultRequest(defaultRequest).withTrackingId(TEST_TRACKING_ID).build(); 118 | 119 | GoogleAnalyticsResponse response = lga.pageView("http://www.google.com", "Search").customDimension(2, "bob").customDimension(5, "alice") 120 | .send(); 121 | 122 | assertEquals("foo", response.getRequestParams().get("cd1")); 123 | assertEquals("bob", response.getRequestParams().get("cd2")); 124 | assertEquals("alice", response.getRequestParams().get("cd5")); 125 | } 126 | 127 | @Test 128 | public void testCustomMetrics() throws Exception { 129 | DefaultRequest defaultRequest = new DefaultRequest(); 130 | defaultRequest.customMetric(1, "foo"); 131 | defaultRequest.customMetric(5, "bar"); 132 | 133 | GoogleAnalytics lga = getTestBuilder().withDefaultRequest(defaultRequest).withTrackingId(TEST_TRACKING_ID).build(); 134 | 135 | GoogleAnalyticsResponse response = lga.pageView("http://www.google.com", "Search").customMetric(2, "bob").customMetric(5, "alice").send(); 136 | 137 | assertEquals("foo", response.getRequestParams().get("cm1")); 138 | assertEquals("bob", response.getRequestParams().get("cm2")); 139 | assertEquals("alice", response.getRequestParams().get("cm5")); 140 | } 141 | 142 | @Test 143 | public void testUserIpAndAgent() throws Exception { 144 | DefaultRequest defaultRequest = new DefaultRequest(); 145 | defaultRequest.userIp("1.2.3.4"); 146 | defaultRequest.userAgent("Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14"); 147 | 148 | GoogleAnalytics lga = getTestBuilder().withDefaultRequest(defaultRequest).withTrackingId(TEST_TRACKING_ID).build(); 149 | 150 | GoogleAnalyticsResponse response = lga.pageView("http://www.google.com", "Search").userIp("1.2.3.5") 151 | .userAgent("Chrome/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14").send(); 152 | 153 | assertEquals("1.2.3.5", response.getRequestParams().get("uip")); 154 | assertEquals("Chrome/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14", response.getRequestParams().get("ua")); 155 | } 156 | 157 | @Test 158 | public void testUserDetails() throws Exception { 159 | GoogleAnalyticsResponse response = ga.pageView("http://www.google.com", "Search").send(); 160 | assertNotNull(response.getRequestParams().get("cid")); 161 | 162 | DefaultRequest defaultRequest = new DefaultRequest(); 163 | defaultRequest.clientId("1234"); 164 | defaultRequest.userId("user1"); 165 | 166 | GoogleAnalytics lga = getTestBuilder().withDefaultRequest(defaultRequest).withTrackingId(TEST_TRACKING_ID).build(); 167 | 168 | response = lga.pageView("http://www.google.com", "Search").send(); 169 | assertEquals("1234", response.getRequestParams().get("cid")); 170 | assertEquals("user1", response.getRequestParams().get("uid")); 171 | 172 | response = lga.pageView("http://www.google.com", "Search").clientId("12345").userId("user2").send(); 173 | assertEquals("12345", response.getRequestParams().get("cid")); 174 | assertEquals("user2", response.getRequestParams().get("uid")); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/GoogleAnalyticsBatchTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static com.brsanthu.googleanalytics.internal.Constants.TEST_TRACKING_ID; 4 | 5 | import java.util.stream.IntStream; 6 | 7 | import org.junit.BeforeClass; 8 | import org.junit.Test; 9 | 10 | public class GoogleAnalyticsBatchTest { 11 | 12 | private static GoogleAnalytics ga = null; 13 | 14 | @BeforeClass 15 | public static void setup() { 16 | ga = GoogleAnalytics.builder().withTrackingId(TEST_TRACKING_ID).withAppName("Junit Test").withAppVersion("1.0.0") 17 | .withConfig(new GoogleAnalyticsConfig().setBatchingEnabled(true).setBatchSize(10)).build(); 18 | } 19 | 20 | @Test 21 | public void testPageView() throws Exception { 22 | IntStream.range(0, 50).forEach(i -> { 23 | ga.pageView("http://www.google.com", "Search").send(); 24 | }); 25 | } 26 | } -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/GoogleAnalyticsConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public class GoogleAnalyticsConfigTest { 8 | 9 | @Test 10 | public void testDefaultConfig() throws Exception { 11 | GoogleAnalyticsConfig config = new GoogleAnalyticsConfig(); 12 | assertEquals("googleanalyticsjava-thread-{0}", config.getThreadNameFormat()); 13 | assertEquals(100, config.getSamplePercentage()); 14 | assertEquals(0, config.getMinThreads()); 15 | assertEquals(5, config.getMaxThreads()); 16 | assertEquals("http://www.google-analytics.com/collect", config.getHttpUrl()); 17 | assertEquals("https://www.google-analytics.com/collect", config.getHttpsUrl()); 18 | assertEquals(80, config.getProxyPort()); 19 | assertEquals(true, config.isDiscoverRequestParameters()); 20 | assertEquals(false, config.isGatherStats()); 21 | } 22 | 23 | @Test 24 | public void testNonDefaultConfig() throws Exception { 25 | GoogleAnalyticsConfig initialConfig = new GoogleAnalyticsConfig() 26 | .setThreadNameFormat("new format") 27 | .setSamplePercentage(50) 28 | .setMinThreads(1) 29 | .setMaxThreads(2) 30 | .setHttpUrl("test http url") 31 | .setHttpsUrl("test https url") 32 | .setProxyPort(42) 33 | .setDiscoverRequestParameters(false) 34 | .setGatherStats(true); 35 | GoogleAnalytics analytics = GoogleAnalytics.builder() 36 | .withConfig(initialConfig).build(); 37 | 38 | GoogleAnalyticsConfig config = analytics.getConfig(); 39 | assertEquals("new format", config.getThreadNameFormat()); 40 | assertEquals(50, config.getSamplePercentage()); 41 | assertEquals(1, config.getMinThreads()); 42 | assertEquals(2, config.getMaxThreads()); 43 | assertEquals("test http url", config.getHttpUrl()); 44 | assertEquals("test https url", config.getHttpsUrl()); 45 | assertEquals(42, config.getProxyPort()); 46 | assertEquals(false, config.isDiscoverRequestParameters()); 47 | assertEquals(true, config.isGatherStats()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/GoogleAnalyticsOkHttpTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static com.brsanthu.googleanalytics.internal.Constants.TEST_TRACKING_ID; 4 | 5 | import org.junit.BeforeClass; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.brsanthu.googleanalytics.httpclient.OkHttpClientImpl; 10 | 11 | public class GoogleAnalyticsOkHttpTest extends GoogleAnalyticsApacheHttpTest { 12 | private static final Logger logger = LoggerFactory.getLogger(GoogleAnalyticsOkHttpTest.class); 13 | 14 | @BeforeClass 15 | public static void setup() { 16 | logger.debug("setup() starting"); 17 | ga = new GoogleAnalyticsOkHttpTest().getTestBuilder().build(); 18 | } 19 | 20 | @Override 21 | protected GoogleAnalyticsBuilder getTestBuilder() { 22 | return GoogleAnalytics.builder() 23 | .withTrackingId(TEST_TRACKING_ID) 24 | .withAppName("Junit OkHttp Test") 25 | .withAppVersion("1.0.0") 26 | .withHttpClient(new OkHttpClientImpl(new GoogleAnalyticsConfig())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/GoogleAnalyticsParameterTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertNull; 6 | 7 | import org.junit.Test; 8 | 9 | import com.brsanthu.googleanalytics.request.GoogleAnalyticsParameter; 10 | 11 | public class GoogleAnalyticsParameterTest { 12 | 13 | @Test 14 | public void testParameters() throws Exception { 15 | assertParameter("v", true, "text", null, 0, GoogleAnalyticsParameter.PROTOCOL_VERSION); 16 | assertParameter("tid", true, "text", null, 0, GoogleAnalyticsParameter.TRACKING_ID); 17 | assertParameter("aip", false, "boolean", null, 0, GoogleAnalyticsParameter.ANONYMIZE_IP); 18 | assertParameter("qt", false, "integer", null, 0, GoogleAnalyticsParameter.QUEUE_TIME); 19 | assertParameter("z", false, "text", null, 0, GoogleAnalyticsParameter.CACHE_BUSTER); 20 | 21 | assertParameter("cid", true, "text", null, 0, GoogleAnalyticsParameter.CLIENT_ID); 22 | assertParameter("uid", false, "text", null, 0, GoogleAnalyticsParameter.USER_ID); 23 | 24 | assertParameter("sc", false, "text", null, 0, GoogleAnalyticsParameter.SESSION_CONTROL); 25 | assertParameter("uip", false, "text", null, 0, GoogleAnalyticsParameter.USER_IP); 26 | assertParameter("ua", false, "text", null, 0, GoogleAnalyticsParameter.USER_AGENT); 27 | assertParameter("aid", false, "text", null, 150, GoogleAnalyticsParameter.APPLICATION_ID); 28 | 29 | assertParameter("ds", false, "text", null, 0, GoogleAnalyticsParameter.DATA_SOURCE); 30 | 31 | assertParameter("aiid", false, "text", null, 150, GoogleAnalyticsParameter.APPLICATION_INSTALLER_ID); 32 | 33 | assertParameter("geoid", false, "text", null, 0, GoogleAnalyticsParameter.GEOID); 34 | } 35 | 36 | public void testParametersOld() throws Exception { 37 | 38 | assertEquals("v", GoogleAnalyticsParameter.PROTOCOL_VERSION.getParameterName()); 39 | assertEquals("gclid", GoogleAnalyticsParameter.ADWORDS_ID.getParameterName()); 40 | assertEquals("aip", GoogleAnalyticsParameter.ANONYMIZE_IP.getParameterName()); 41 | assertEquals("an", GoogleAnalyticsParameter.APPLICATION_NAME.getParameterName()); 42 | assertEquals("av", GoogleAnalyticsParameter.APPLICATION_VERSION.getParameterName()); 43 | assertEquals("z", GoogleAnalyticsParameter.CACHE_BUSTER.getParameterName()); 44 | assertEquals("cc", GoogleAnalyticsParameter.CAMPAIGN_CONTENT.getParameterName()); 45 | assertEquals("ci", GoogleAnalyticsParameter.CAMPAIGN_ID.getParameterName()); 46 | assertEquals("ck", GoogleAnalyticsParameter.CAMPAIGN_KEYWORD.getParameterName()); 47 | assertEquals("cm", GoogleAnalyticsParameter.CAMPAIGN_MEDIUM.getParameterName()); 48 | assertEquals("cn", GoogleAnalyticsParameter.CAMPAIGN_NAME.getParameterName()); 49 | assertEquals("cs", GoogleAnalyticsParameter.CAMPAIGN_SOURCE.getParameterName()); 50 | assertEquals("cid", GoogleAnalyticsParameter.CLIENT_ID.getParameterName()); 51 | assertEquals("uid", GoogleAnalyticsParameter.USER_ID.getParameterName()); 52 | assertEquals("cd", GoogleAnalyticsParameter.CONTENT_DESCRIPTION.getParameterName()); 53 | assertEquals("cu", GoogleAnalyticsParameter.CURRENCY_CODE.getParameterName()); 54 | assertEquals("dclid", GoogleAnalyticsParameter.DISPLAY_ADS_ID.getParameterName()); 55 | assertEquals("dns", GoogleAnalyticsParameter.DNS_TIME.getParameterName()); 56 | assertEquals("de", GoogleAnalyticsParameter.DOCUMENT_ENCODING.getParameterName()); 57 | assertEquals("dh", GoogleAnalyticsParameter.DOCUMENT_HOST_NAME.getParameterName()); 58 | assertEquals("dl", GoogleAnalyticsParameter.DOCUMENT_URL.getParameterName()); 59 | assertEquals("dp", GoogleAnalyticsParameter.DOCUMENT_PATH.getParameterName()); 60 | assertEquals("dr", GoogleAnalyticsParameter.DOCUMENT_REFERRER.getParameterName()); 61 | assertEquals("dt", GoogleAnalyticsParameter.DOCUMENT_TITLE.getParameterName()); 62 | assertEquals("ea", GoogleAnalyticsParameter.EVENT_ACTION.getParameterName()); 63 | assertEquals("ec", GoogleAnalyticsParameter.EVENT_CATEGORY.getParameterName()); 64 | assertEquals("el", GoogleAnalyticsParameter.EVENT_LABEL.getParameterName()); 65 | assertEquals("ev", GoogleAnalyticsParameter.EVENT_VALUE.getParameterName()); 66 | assertEquals("exd", GoogleAnalyticsParameter.EXCEPTION_DESCRIPTION.getParameterName()); 67 | assertEquals("exf", GoogleAnalyticsParameter.EXCEPTION_FATAL.getParameterName()); 68 | assertEquals("fl", GoogleAnalyticsParameter.FLASH_VERSION.getParameterName()); 69 | assertEquals("t", GoogleAnalyticsParameter.HIT_TYPE.getParameterName()); 70 | assertEquals("iv", GoogleAnalyticsParameter.ITEM_CATEGORY.getParameterName()); 71 | assertEquals("ic", GoogleAnalyticsParameter.ITEM_CODE.getParameterName()); 72 | assertEquals("in", GoogleAnalyticsParameter.ITEM_NAME.getParameterName()); 73 | assertEquals("ip", GoogleAnalyticsParameter.ITEM_PRICE.getParameterName()); 74 | assertEquals("iq", GoogleAnalyticsParameter.ITEM_QUANTITY.getParameterName()); 75 | assertEquals("je", GoogleAnalyticsParameter.JAVA_ENABLED.getParameterName()); 76 | assertEquals("ni", GoogleAnalyticsParameter.NON_INTERACTION_HIT.getParameterName()); 77 | assertEquals("pdt", GoogleAnalyticsParameter.PAGE_DOWNLOAD_TIME.getParameterName()); 78 | assertEquals("plt", GoogleAnalyticsParameter.PAGE_LOAD_TIME.getParameterName()); 79 | assertEquals("qt", GoogleAnalyticsParameter.QUEUE_TIME.getParameterName()); 80 | assertEquals("rrt", GoogleAnalyticsParameter.REDIRECT_RESPONSE_TIME.getParameterName()); 81 | assertEquals("sd", GoogleAnalyticsParameter.SCREEN_COLORS.getParameterName()); 82 | assertEquals("sr", GoogleAnalyticsParameter.SCREEN_RESOLUTION.getParameterName()); 83 | assertEquals("srt", GoogleAnalyticsParameter.SERVER_RESPONSE_TIME.getParameterName()); 84 | assertEquals("sc", GoogleAnalyticsParameter.SESSION_CONTROL.getParameterName()); 85 | assertEquals("sa", GoogleAnalyticsParameter.SOCIAL_ACTION.getParameterName()); 86 | assertEquals("st", GoogleAnalyticsParameter.SOCIAL_ACTION_TARGET.getParameterName()); 87 | assertEquals("sn", GoogleAnalyticsParameter.SOCIAL_NETWORK.getParameterName()); 88 | assertEquals("tcp", GoogleAnalyticsParameter.TCP_CONNECT_TIME.getParameterName()); 89 | assertEquals("tid", GoogleAnalyticsParameter.TRACKING_ID.getParameterName()); 90 | assertEquals("ta", GoogleAnalyticsParameter.TRANSACTION_AFFILIATION.getParameterName()); 91 | assertEquals("ti", GoogleAnalyticsParameter.TRANSACTION_ID.getParameterName()); 92 | assertEquals("tr", GoogleAnalyticsParameter.TRANSACTION_REVENUE.getParameterName()); 93 | assertEquals("ts", GoogleAnalyticsParameter.TRANSACTION_SHIPPING.getParameterName()); 94 | assertEquals("tt", GoogleAnalyticsParameter.TRANSACTION_TAX.getParameterName()); 95 | assertEquals("ul", GoogleAnalyticsParameter.USER_LANGUAGE.getParameterName()); 96 | assertEquals("utc", GoogleAnalyticsParameter.USER_TIMING_CATEGORY.getParameterName()); 97 | assertEquals("utl", GoogleAnalyticsParameter.USER_TIMING_LABEL.getParameterName()); 98 | assertEquals("utt", GoogleAnalyticsParameter.USER_TIMING_TIME.getParameterName()); 99 | assertEquals("utv", GoogleAnalyticsParameter.USER_TIMING_VARIABLE_NAME.getParameterName()); 100 | assertEquals("vp", GoogleAnalyticsParameter.VIEWPORT_SIZE.getParameterName()); 101 | assertEquals("uip", GoogleAnalyticsParameter.USER_ID.getParameterName()); 102 | assertEquals("ua", GoogleAnalyticsParameter.USER_AGENT.getParameterName()); 103 | 104 | assertEquals("xid", GoogleAnalyticsParameter.EXPERIMENT_ID.getParameterName()); 105 | assertNull(GoogleAnalyticsParameter.EXPERIMENT_ID.getSupportedHitTypes()); 106 | assertEquals("text", GoogleAnalyticsParameter.EXPERIMENT_ID.getType()); 107 | 108 | assertEquals("xvar", GoogleAnalyticsParameter.EXPERIMENT_VARIANT.getParameterName()); 109 | assertNull(GoogleAnalyticsParameter.EXPERIMENT_VARIANT.getSupportedHitTypes()); 110 | assertEquals("text", GoogleAnalyticsParameter.EXPERIMENT_VARIANT.getType()); 111 | } 112 | 113 | private void assertParameter(String name, boolean required, String type, String[] hitTypes, int maxLength, GoogleAnalyticsParameter param) { 114 | assertEquals(name, param.getParameterName()); 115 | assertEquals(required, param.isRequired()); 116 | assertEquals(type, param.getType()); 117 | assertArrayEquals(hitTypes, param.getSupportedHitTypes()); 118 | assertEquals(maxLength, param.getMaxLength()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/GoogleAnalyticsRequestTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import com.brsanthu.googleanalytics.request.PageViewHit; 8 | 9 | public class GoogleAnalyticsRequestTest { 10 | 11 | @Test 12 | public void testRequestSetterGetter() throws Exception { 13 | PageViewHit request = new PageViewHit(); 14 | 15 | assertEquals("1.2.3.4", request.userIp("1.2.3.4").userIp()); 16 | assertEquals("Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14", 17 | request.userAgent("Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14").userAgent()); 18 | } 19 | 20 | @Test 21 | public void testExperimentArgs() throws Exception { 22 | PageViewHit pageViewHit = new PageViewHit().experimentId("1234567890").experimentVariant("some variation"); 23 | assertEquals("1234567890", pageViewHit.experimentId()); 24 | assertEquals("some variation", pageViewHit.experimentVariant()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/HitTypesTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import com.brsanthu.googleanalytics.request.EventHit; 8 | import com.brsanthu.googleanalytics.request.ExceptionHit; 9 | import com.brsanthu.googleanalytics.request.ItemHit; 10 | import com.brsanthu.googleanalytics.request.PageViewHit; 11 | import com.brsanthu.googleanalytics.request.ScreenViewHit; 12 | import com.brsanthu.googleanalytics.request.SocialHit; 13 | import com.brsanthu.googleanalytics.request.TimingHit; 14 | import com.brsanthu.googleanalytics.request.TransactionHit; 15 | 16 | public class HitTypesTest { 17 | 18 | @Test 19 | public void testHitTypes() throws Exception { 20 | assertEquals("item", new ItemHit().hitType()); 21 | assertEquals("screenview", new ScreenViewHit().hitType()); 22 | assertEquals("event", new EventHit().hitType()); 23 | assertEquals("exception", new ExceptionHit().hitType()); 24 | assertEquals("pageview", new PageViewHit().hitType()); 25 | assertEquals("social", new SocialHit().hitType()); 26 | assertEquals("timing", new TimingHit().hitType()); 27 | assertEquals("transaction", new TransactionHit().hitType()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/brsanthu/googleanalytics/SamplingTest.java: -------------------------------------------------------------------------------- 1 | package com.brsanthu.googleanalytics; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.mockito.ArgumentMatchers; 9 | import org.mockito.Mockito; 10 | 11 | import com.brsanthu.googleanalytics.httpclient.HttpClient; 12 | import com.brsanthu.googleanalytics.internal.GoogleAnalyticsImpl; 13 | import com.brsanthu.googleanalytics.request.ScreenViewHit; 14 | 15 | public class SamplingTest { 16 | private HttpClient mockHttpClient = null; 17 | 18 | @Before 19 | public void beforeEachTest() { 20 | // construct a mock HttpClient so we can check hit results 21 | mockHttpClient = Mockito.mock(HttpClient.class); 22 | } 23 | 24 | @Test 25 | public void testSampleElectionDefault() { 26 | 27 | // With the default 100% sample everyone should be in 28 | GoogleAnalytics analytics = getDefaultAnalytics(); 29 | assertTrue(analytics.inSample()); 30 | } 31 | 32 | @Test 33 | public void testSampleElectionZero() { 34 | // Make sure that if we set a zero percentage we are not in the sample 35 | GoogleAnalytics analytics = getOutOfSampleAnalytics(); 36 | assertFalse(analytics.inSample()); 37 | } 38 | 39 | @Test 40 | public void testInSampleSend() { 41 | GoogleAnalyticsImpl analytics = (GoogleAnalyticsImpl)getDefaultAnalytics(); 42 | ScreenViewHit hit = analytics.screenView().screenName("test screen"); 43 | try { 44 | hit.send(); 45 | } catch (Exception e) { 46 | // this throws now, but will still verify that we got past our in-sample check 47 | } 48 | Mockito.verify(mockHttpClient).post(ArgumentMatchers.any()); 49 | } 50 | 51 | @Test 52 | public void testNotInSampleHit() { 53 | GoogleAnalytics analytics = getOutOfSampleAnalytics(); 54 | ScreenViewHit hit = analytics.screenView().screenName("test screen"); 55 | hit.send(); 56 | Mockito.verifyNoInteractions(mockHttpClient); 57 | } 58 | 59 | @Test 60 | public void fuzzyTestRandomness() { 61 | GoogleAnalytics fiftyPercentAnalytics = GoogleAnalytics.builder() 62 | .withConfig(new GoogleAnalyticsConfig().setSamplePercentage(50)) 63 | .build(); 64 | int inSampleCount = 0; 65 | for (int i = 0; i < 1000; i++) { 66 | if (fiftyPercentAnalytics.performSamplingElection()) { 67 | assertTrue(fiftyPercentAnalytics.inSample()); 68 | inSampleCount++; 69 | } else { 70 | assertFalse(fiftyPercentAnalytics.inSample()); 71 | } 72 | } 73 | assertTrue("Possibly flaky - sample out of range: " + inSampleCount, 450 <= inSampleCount && inSampleCount <= 550); 74 | } 75 | 76 | private GoogleAnalytics getDefaultAnalytics() { 77 | return GoogleAnalytics.builder().withHttpClient(mockHttpClient).build(); 78 | } 79 | 80 | private GoogleAnalytics getOutOfSampleAnalytics() { 81 | return GoogleAnalytics.builder() 82 | .withConfig(new GoogleAnalyticsConfig().setSamplePercentage(0)) 83 | .withHttpClient(mockHttpClient) 84 | .build(); 85 | } 86 | } 87 | --------------------------------------------------------------------------------