Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreThis post is a guest post by community member Tadaya Tsuyukubo (@ttddyy), creator of the Spring Social Slideshare project. Thanks Tadaya! I’d like to see more of these guest posts, so - as usual - don’t hesitate to ping me! -Josh
Spring Social Evernote is one of the community modules in the Spring Social ecosystem. It is a service provider implementation for Evernote. It allows developers to work with the Evernote SDK for Java with idiomatic Spring idioms.
Evernote takes a unique approach for providing their APIs to developers. They have created language specific SDKs based on Thrift serialization format. Dave Engberg, CTO of Evernote, explained the motivations for choosing Thrift in this blog.
The Java SDK includes Thrift-generated domain classes (e.g. Notebook
, Note
, User
, etc.) and endpoint access clients (e.g. UserStoreClient
, NoteStoreClient
, etc). Even though ~StoreClient
classes nicely encapsulate authentication and Thrift protocol for endpoint communication, they still have a few limitations.
For example, all Thrift methods that do communication in the various ~StoreClient
types throw checked exceptions. Also, ~StoreClient
classes don’t have interfaces corresponding to their operations, which frustrates (somewhat) unit testing.
Spring Social Evernote fills these gaps while delivering a familiar Spring Social programming model.
Spring Social Evernote delivers a familiar Spring and Java-based programmingmodel that uses interfaces, unchecked exceptions, and null
-safe Collection
s for Thrift-based domain objects. It also provides a service provider implementation for the Evernote OAuth API.
The Evernote
interface and it’s implementation, EvernoteTemplate
, are the central types for the API. The Evernote
interface returns ~StoreOperations
(e.g. NoteStoreOperations
, or UserStoreOperations
) interfaces that correspond to the SDK's ~StoreClient
classes. Methods on these interfaces throw unchecked EvernoteException
instead of checked EDAM*Exception
. These implementations also have smarter null
-value handling for collections in Thrift-generated domain types.
Evernote evernote = new EvernoteTemplate(
EvernoteService.SANDBOX, "your-custom-access-token");
// interface based programming
NoteStoreOperations noteStore = evernote.noteStoreOperations();
// no checked exception is thrown
Notebook notebook = noteStore.getDefaultNotebook();
// no NPE when there are no shared notebooks
for (SharedNotebook sharedNotebook : notebook.getSharedNotebooks()) {
...
}
~StoreClientOperations
interface classes correspond to ~StoreClient
implementation classes (e.g. NoteStoreClientOperations
corresponds to com.evernote.clients.NoteStoreClient
).
// NoteStoreOperations is interface encapsulating NoteStoreClient
NoteStoreOperations noteStore = evernote.noteStoreClientOperations();
Notebook notebook = noteStore.getNotebook("...");
EDAM exceptions (e.g. com.evernote.edam.error.EDAMUserException
) and Thrift exceptions (e.g. com.evernote.Thrift.TException
) are trapped and rethrown as runtime exceptions of type EvernoteException
.
// with UserStoreOperations, no explicit exception handling is required
User user = evernote.userStoreOperations().getUser();
// or explicitly you can get exception info
try {
User user = userStoreClient.getUser();
} catch(EvernoteException e) {
if (e.isEDAMUserException()) {
EDAMErrorCode errorCode = e.getEDAMErrorCode();
EDAMUserException originalException = e.getCause();
....
}
}
In Thrift, a null
collection isn't serialized, but the convention can cause some unexpected NullPointerException
s when using the API. The Spring Social Evernote implementation sanitizes these values with empty collections.
Note note = evernote.noteStoreOperations().getNote(...)
// it is safe to loop without null check
for(Resource resource: note.getResources()) {
...
}
or (in Java 8):
evernote.noteStoreOperations()
.getNote()
.getResources()
.forEach ( r -> System.out.println( r.toString() ));
Spring Social Evernote dirty checks the types and restores their null
values just before serialization if the collection is otherwise empty.
For more on this particular support, check out nullsafe-Thrift - a proof-of-concept project.
Registering ConnectionFactory
To register Evernote environment (sandbox
, prod
, yinxiang
) into Spring Social Service Provider Connect Framework, you can use environment-specific ConnectionFactory
classes like EvernoteSandboxConnectionFactory
, EvernoteProductionConnectionFactory
, or EvernoteYinXiangConnectionFactory
. Alternatively, you can pass EvernoteService
enum to EvernoteConnectionFactory
in constructor.
ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
// sandbox connection
registry.addConnectionFactory(new EvernoteSandboxConnectionFactory("consumerKey", "consumerSecret"));
// production connection
registry.addConnectionFactory(new EvernoteProductionConnectionFactory("consumerKey", "consumerSecret"));
// or by EvernoteService enum
registry.addConnectionFactory(new EvernoteConnectionFactory("consumerKey", "consumerSecret", EvernoteService.SANDBOX));
Performing Evernote-based OAuth authentication works as follows:
// obtain request token (temporal credential)
OAuth1Operations oauthOperations = evernoteConnectionFactory.getOAuthOperations();
OAuthToken requestToken = oauthOperations.fetchRequestToken(
callbackUrl, null);
// construct authorization url with callback url for client to redirect
OAuth1Parameters parameters = new OAuth1Parameters();
parameters.set("preferRegistration", "true"); // create account
parameters.set("supportLinkedSandbox", "true");
String authorizeUrl = oauthOperations.buildAuthorizeUrl(
requestToken.getValue(), parameters);
// obtain access token
OAuthToken requestToken = new OAuthToken(oauthToken, requestTokenSecret);
AuthorizedRequestToken authorizedRequestToken = new AuthorizedRequestToken(requestToken, oauthVerifier);
OAuth1Operations oAuth1Operations = evernoteConnectionFactory.getOAuthOperations(); // EvernoteOAuth1Operations
EvernoteOAuthToken accessToken = (EvernoteOAuthToken)oAuth1Operations.exchangeForAccessToken(authorizedRequestToken, null);
For more project documentation, check out the project wiki page. Also, nullsafe collection in Thrift can be demonstrated in this test class as well.
Since Evernote doesn’t have RESTful API, I have created a web application, Evernote Rest Webapp, which provides RESTful endpoints for Evernote services. It is built on top of Spring Boot and Spring Social Evernote. This implementation serves as a sort of bridge-API if you're interested.
Spring Social Evernote provides modern programming model and easy integration with Evernote authentication to your application.
If you have any suggestion, question, or anything, please ping me @ttddyy.