Strava API v3 implementation written in Java v8
Javastrava is a functionally complete implementation of the Strava API (v3). It includes all the changes made to the API up to May 13, 2016.
It consists of 2 layers which implement the API:
javastrava is available on Maven. Just add this to your POM:
<dependency>
<groupId>com.github.danshannon</groupId>
<artifactId>javastrava-api</artifactId>
<version>1.0.3</version>
</dependency>
To use Javastrava, all you really need is an access token. Javastrava doesn't provide a programmatic mechanism for acquiring one via OAuth because you need to get your end user to agree to your specific use of their Strava data, and therefore they should do that personally. You will need to register with Strava for an API key, and you will get an API key (access token) automatically when you register your app. There is a hack which gets tokens through the OAuth process on the Strava website built into the test suite, see test.utils.TestUtils.getValidToken()
, but remember that it's a hack!
Basically, once you've gone through the OAuth validation process, Strava returns a one-off code. You then need to exchange that code for a token:
AuthorisationService service = new AuthorisationServiceImpl();
Token token = service.tokenExchange({application_client_id}, {client_secret}, code);
Once you've got an access token, life is pretty simple really. Getting a service implementation looks like this:
Strava strava = new Strava(token);
Then, getting an athlete looks like this:
StravaAthlete athlete = strava.getAthlete(id);
If you prefer to use the raw API, then a similar approach is required. Again, it's your problem to get through the OAuth process until you've got a code. Then, to get a token:
AuthorisationAPI auth = API.authorisationInstance();
TokenResponse response = auth.tokenExchange({application_client_id}, {client_secret}, code);
Token token = new Token(response);
Now we can get an API instance:
API api = new API(token);
And finally, the athlete:
StravaAthlete athlete = api.getAthlete(id);
We've also implemented an asynchronous version of the API.
Its use is very similar to the synchronous API, but instead the asynchronous methods return a CompletableFuture
that you can call later to retrieve the results, after doing something else
API api = new API(token)
CompletableFuture<StravaAthlete> future = api.getAthleteAsync(id);
// Now you can do something else while you wait for the result
doSomethingInterestingInsteadOfWaiting();
// And when you're ready, get the athlete from the future...
StravaAthlete athlete = future.complete();
The full implementation provides a caching mechanism to reduce the overall number of calls to the Strava API. The caching mechanism uses Apache JCS and your application will need to provide a cache.ccf configuration file to make use of it.
The raw API does not cache data.
The TokenManager
class provides a cache for all active tokens, for all users who have given permission to your application. Token exchange (above) will add each token to the token manager via TokenManager.instance().storeToken(token)
.
You can then retrieve a token from the TokenManager later on via TokenManager.instance().retrieveToken(username)
. The username is the email address that the user logs in to Strava with; you can find it with token.getAthlete().getEmail()
The API doesn't currently cater for persistence of tokens or of the token manager; that's up to your application to do.
The Strava API can be a bit, well, weird when you use it in anger. The interaction between privacy settings, authentication and so on isn't always consistent in the API. What we've done is this:
null
. API methods will throw a NotFoundException
UnauthorisedException
InvalidTokenException
(which is unchecked) get thrownUnauthorisedException
(which is unchecked) get thrown.StravaAPINetworkException
is thrown.StravaAPIRateLimitException
.503 Service Temporarily Unavailable
status, then you'll see a StravaServiceUnavailableException
.500 Internal Server Error
, then you'll get a StravaInternalServerErrorException
.StravaUnknownAPIException
to be thrown.We've provided a stack of alternate method signatures for all the API endpoints, both with and without the paging options.
The methods that do not include paging instructions will return only the first page from the Strava API, not everything. There are methods that do return everything, they're typically called listAll*
. Be careful using these...
The methods that do include paging instructions are built to override the Strava paging limits. If you really want, you can ask for 10,000 or more activities at once, not Strava's artificial limit of 200 per page. Be aware, though, that internally we're still bound by the Strava limits, so asking for 10,000 activities will result in 50 calls to the API! That's going to exhaust your throttling limits (by default 600 calls every 15 minutes) pretty fast...
Obviously doing many sequential calls to the API to return all of something would be extremely slow, so the calls to the API are executed in parallel. See javastrava.util.PagingHandler
for details of how this is done.
To use the paging options, you pass in a stravajava.util.Paging object as the pagingInstruction parameter. Have a look; it's amazimgly flexible!
The Strava API is annoying when it passes your own results along with every page of a leaderboard. We've hacked that out, so that in the stravajava.api.v3.model.StravaSegmentLeaderboard
definitions you'll see there are 2 collections of entries - entries
is the one that you actually asked for, athleteEntries
is the one that relates to the 5 entries around the authenticated athlete / you. Should be much simpler to deal with!
There's a test suite at https://github.com/danshannon/javastrava-test
The Stravajava API is dependent on Java 8 runtime as it uses the new asynchronous processing model (specifically, CompletableFuture). It won't compile on Java 7 or below and at this stage there's no intention to backport it (although if you want to, feel free - should only need to remove all the asynchronous bits).