3. Adding Support for a New Service Provider

Spring Social makes it easy to add support for service providers that are not already supported by the framework. If you review the existing client modules, such as spring-social-twitter and spring-social-facebook, you will discover they are implemented in a consistent manner and they apply a set of well-defined extension points. In this chapter, you will learn how to add support for new service providers you wish to integrate into your applications.

3.1 Process overview

The process of adding support for a new service provider consists of several steps:

  1. Create a source project for the client code e.g. spring-social-twitter.

  2. Develop or integrate a Java binding to the provider's API e.g. Twitter.

  3. Create a ServiceProvider model that allows users to authorize with the remote provider and obtain authorized API instances e.g. TwitterServiceProvider.

  4. Create an ApiAdapter that maps the provider's native API onto the uniform Connection model e.g. TwitterAdapter.

  5. Finally, create a ConnectionFactory that wraps the other artifacts up and provides a simple interface for establishing connections e.g. TwitterConnectionFactory.

The following sections of this chapter walk you through each of the steps with examples.

3.2 Creating a source project for the provider client code

A Spring Social client module is a standard Java project that builds a single jar artifact e.g. spring-social-twitter.jar. We recommend the code structure of a client module follow the guidelines described below.

3.2.1 Code structure guidelines

We recommend the code for a new Spring Social client module reside within the org.springframework.social.{providerId} base package, where {providerId} is a unique identifier you assign to the service provider you are adding support for. Consider some of the providers already supported by the framework as examples:

Table 3.1. Spring Social Client Modules

Provider IDArtifact NameBase Package
facebookspring-social-facebookorg.springframework.social.facebook
twitterspring-social-twitterorg.springframework.social.twitter


Within the base package, we recommend the following subpackage structure:

Table 3.2. Module Structure

SubpackageDescription
apiThe public interface that defines the API binding.
api.implThe implementation of the API binding.
connectThe types necessary to establish connections to the service provider.


You can see this recommended structure in action by reviewing one of the other client modules such as spring-social-twitter:

Here, the central service API type, Twitter, is located in the api package along with its supporting operations types and data transfer object types. The primary implementation of that interface, TwitterTemplate, is located in the api.impl package (along with other package-private impl types have that been excluded from this view). Finally, the connect package contains the implementations of various connect SPIs that enable connections to Twitter to be established and persisted.

3.3 Developing a Java binding to the provider's API

Spring Social favors the development of strongly-typed Java bindings to external service provider APIs. This provides a simple, domain-oriented interface for Java applications to use to consume the API. When adding support for a new service provider, if no suitable Java binding already exists you'll need to develop one. If one already exists, such as Twitter4j for example, it is possible to integrate it into the framework.

3.3.1 Designing a new Java API binding

API developers retain full control over the design and implementation of their Java bindings. That said, we offer several design guidelines in an effort to improve overall consistency and quality:

  • Favor separating the API binding interface from the implementation. This is illustrated in the spring-social-twitter example in the previous section. There, "Twitter" is the central API binding type and it is declared in the org.springframework.social.twitter.api package with other public types. "TwitterTemplate" is the primary implementation of this interface and is located in the org.springframework.social.twitter.api.impl subpackage along with other package-private implementation types.

  • Favor organizing the API binding hierarchically by RESTful resource. REST-based APIs typically expose access to a number of resources in an hierarchical manner. For example, Twitter's API provides access to "status timelines", "searches", "lists", "direct messages", "friends", "geo location", and "users". Rather than add all operations across these resources to a single flat "Twitter" interface, the Twitter interface is organized hierarchically:

    public interface Twitter extends ApiBinding {
    	
        DirectMessageOperations directMessageOperations();
    
        FriendOperations friendOperations();
    
        GeoOperations geoOperations();
    
        ListOperations listOperations();
    
        SearchOperations searchOperations();
    	
        TimelineOperations timelineOperations();
    
        UserOperations userOperations();
    
    }

    DirectMessageOperations, for example, contains API bindings to Twitter's "direct_messages" resource:

    public interface DirectMessageOperations {
    
        List<DirectMessage> getDirectMessagesReceived();
    
        List<DirectMessage> getDirectMessagesSent();
    
        void sendDirectMessage(String toScreenName, String text);
    
        void sendDirectMessage(long toUserId, String text);
    	
        void deleteDirectMessage(long messageId);
    }
    							

3.3.2 Implementing a new Java API binding

API developers are free to implement their Java API binding with whatever REST/HTTP client they see fit. That said, Spring Social's existing API bindings such as spring-social-twitter all use Spring Framework's RestTemplate in conjunction with the Jackson JSON ObjectMapper and Apache HttpComponents HTTP client. RestTemplate is a popular REST client that provides a uniform object mapping interface across a variety of data exchange formats (JSON, XML, etc). Jackson is the leading Java-based JSON marshalling technology. Apache HttpComponents has proven to be the most robust HTTP client (if it is not available on the classpath Spring Social will fallback to standard J2SE facilities, however). To help promote consistency across Spring Social's supported bindings, we do recommend you consider these implementation technologies (and please let us know if they do not meet your needs).

Spring Social has adopted a convention where each API implementation class is named "{ProviderId}Template" e.g. TwitterTemplate. We favor this convention unless there is a good reason to deviate from it. As discussed in the previous section, we recommend keeping implementation types separate from the public API types. We also recommend keeping internal implementation details package-private.

The way in which an API binding implementation is constructed will vary based on the API's authorization protocol. For APIs secured with OAuth1, the consumerKey, consumerSecret, accessToken, and accessTokenSecret will be required for construction:

public TwitterTemplate(String consumerKey, String consumerSecret, String accessToken, 
    String accessTokenSecret) { ... }
				

For OAuth2, only the access token should be required:

public FacebookTemplate(String accessToken) { ... }
				

Each request made to the API server needs to be signed with the authorization credentials provided during construction of the binding. This signing process consists of adding an "Authorization" header to each client request before it is executed. For OAuth1, the process is quite complicated, and is used to support an elaborate request signature verification algorithm between the client and server. For OAuth2, it is a lot simpler, but does still vary across the various drafts of the OAuth2 specification.

To encapsulate this complexity, for each authorization protocol Spring Social provides a ApiTemplate base class you may extend from to construct a pre-configured RestTemplate instance that performs the request signing for you. For OAuth1:

public class TwitterTemplate extends AbstractOAuth1ApiBinding {
    public TwitterTemplate(String consumerKey, String consumerSecret, String accessToken, 
            String accessTokenSecret) {
        super(consumerKey, consumerSecret, accessToken, accessTokenSecret);
    }
}			

An OAuth2 example:

public class FacebookTemplate extends AbstractOAuth2ApiBinding {
    public FacebookTemplate(String accessToken) {
        super(accessToken);
    }
}
				

Once configured as shown above, you simply implement call getRestTemplate() and implement the various API operations. The existing Spring Social client modules all invoke their RestTemplate instances in a standard manner:

public TwitterProfile getUserProfile() {
    return getRestTemplate().getForObject(buildUri("account/verify_credentials.json"),
        TwitterProfile.class);
}
				

A note on RestTemplate usage: we do favor the RestTemplate methods that accept a URI object instead of a uri String. This ensures we always properly encode client data submitted in URI query parameters, such as screen_name below:

public TwitterProfile getUserProfile(String screenName) {
    return getRestTemplate().getForObject(buildUri("users/show.json", 
        Collections.singletonMap("screen_name", screenName)), TwitterProfile.class);
}
				

For complete implementation examples, consult the source of the existing API bindings included in Spring Social. The spring-social-twitter and spring-social-facebook modules provide particularly good references.

3.3.3 Testing a new Java API binding

As part of the spring-social-test module, Spring Social includes a framework for unit testing API bindings. This framework consists of a "MockRestServiceServer" that can be used to mock out API calls to the remote service provider. This allows for the development of independent, performant, automated unit tests that verify client API binding and object mapping behavior.

To use, first create a MockRestServiceServer against the RestTemplate instance used by your API implementation:

TwitterTemplate twitter = new TwitterTemplate("consumerKey", "consumerSecret", "accessToken",
    "accessTokenSecret");
MockRestServer mockServer = MockRestServiceServer.createServer(twitter.getRestTemplate());
			

Then, for each test case, record expectations about how the server should be invoked and answer what it should respond with:

@Test
public void getUserProfile() {
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.APPLICATION_JSON);
    
    mockServer.expect(requestTo("https://api.twitter.com/1/account/verify_credentials.json"))
        .andExpect(method(GET))
        .andRespond(withResponse(jsonResource("verify-credentials"), responseHeaders));

    TwitterProfile profile = twitter.userOperations().getUserProfile();
    assertEquals(161064614, profile.getId());
    assertEquals("kdonald", profile.getScreenName());
}
			

In the example above the response body is written from a verify-credentials.json file located in the same package as the test class:

private Resource jsonResource(String filename) {
    return new ClassPathResource(filename + ".json", getClass());
}
			

The content of the file should mirror the content the remote service provider would return, allowing the client JSON deserialization behavior to be fully tested:

{
    "id":161064614,
    "screen_name":"kdonald"
}
			

For complete test examples, consult the source of the existing API bindings included in Spring Social. The spring-social-twitter and spring-social-facebook modules provide particularly good references.

3.3.4 Integrating an existing Java API binding

If you are adding support for a popular service provider, chances are a Java binding to the provider's API may already exist. For example, the Twitter4j library has been around for awhile and provides a complete binding to Twitter's API. Instead of developing your own binding, you may simply wish to integrate what already exists. Spring Social's connect framework has been carefully designed to support this scenario.

To integrate an existing API binding, simply note the binding's primary API interface and implementation. For example, in Twitter4j the main API interface is named "Twitter" and instances are constructed by a TwitterFactory. You can always construct such an API instance directly, and you'll see in the following sections how to expose an instance as part of a Connection.

3.4 Creating a ServiceProvider model

As described in the previous section, a client binding to a secure API such as Facebook or Twitter requires valid user authorization credentials to work. Such credentials are generally obtained by having your application conduct an authorization "dance" or handshake with the service provider. Spring Social provides the ServiceProvider<A> abstraction to handle this "authorization dance". The abstraction also acts as a factory for native API (A) instances.

Since the authorization dance is protocol-specific, a ServiceProvider specialization exists for each authorization protocol. For example, if you are connecting to a OAuth2-based provider, you would implement OAuth2ServiceProvider. After you've done this, your implementation can be used to conduct the OAuth2 dance and obtain an authorized API instance. This is typically done in the context of a ConnectionFactory as part of establishing a new connection to the provider. The following sections describe the implementation steps for each ServiceProvider type.

3.4.1 OAuth2

To implement an OAuth2-based ServiceProvider, first create a subclass of AbstractOAuth2ServiceProvider named {ProviderId}ServiceProvider. Parameterize <A> to be the Java binding to the ServiceProvider's's API. Define a single constructor that accepts an clientId and clientSecret. Finally, implement getApi(String) to return a new API instance.

See org.springframework.social.facebook.connect.FacebookServiceProvider as an example OAuth2ServiceProvider:

public final class FacebookServiceProvider extends AbstractOAuth2ServiceProvider<Facebook> {

    public FacebookServiceProvider(String clientId, String clientSecret) {
        super(new OAuth2Template(clientId, clientSecret,
            "https://graph.facebook.com/oauth/authorize",
            "https://graph.facebook.com/oauth/access_token"));
    }

    public Facebook getApi(String accessToken) {
        return new FacebookTemplate(accessToken);
    }

}
			

In the constructor, you should call super, passing up the configured OAuth2Template that implements OAuth2Operations. The OAuth2Template will handle the "OAuth dance" with the provider, and should be configured with the provided clientId and clientSecret, along with the provider-specific authorizeUrl and accessTokenUrl.

Some providers support provider sign-in (see Chapter 5, Signing in with Service Provider Accounts) through an authentication URL that is distinct from the authorization URL. Using the OAuth2Template constructor as shown above will assume that the authentication URL is the same as the authorization URL. But you may specify a different authentication URL by using OAuth2Template's other constructor. Facebook does not have a separate authentication URL, but for the sake of the example, suppose that Facebook's authentication URL is "https://graph.facebook.com/oauth/authenticate". The following implementation of the FacebookServiceProvider constructor configures the OAuth2Template for that case:

public FacebookServiceProvider(String clientId, String clientSecret) {
    super(new OAuth2Template(clientId, clientSecret,
        "https://graph.facebook.com/oauth/authorize",
        "https://graph.facebook.com/oauth/authenticate",
        "https://graph.facebook.com/oauth/access_token"));
}
			

In getApi(String), you should construct your API implementation, passing it the access token needed to make authorized requests for protected resources.

3.4.2 OAuth1

To implement an OAuth1-based ServiceProvider, first create a subclass of AbstractOAuth1ServiceProvider named {ProviderId}ServiceProvider. Parameterize <A> to be the Java binding to the ServiceProvider's API. Define a single constructor that accepts a consumerKey and consumerSecret. Finally, implement getApi(String, String) to return a new API instance.

See org.springframework.social.twitter.connect.TwitterServiceProvider as an example OAuth1ServiceProvider:

public final class TwitterServiceProvider extends AbstractOAuth1ServiceProvider<Twitter> {

    public TwitterServiceProvider(String consumerKey, String consumerSecret) {
        super(consumerKey, consumerSecret, new OAuth1Template(consumerKey, consumerSecret, 
            "https://twitter.com/oauth/request_token",
            "https://twitter.com/oauth/authorize",
            "https://twitter.com/oauth/authenticate",
            "https://twitter.com/oauth/access_token"));
    }

    public Twitter getApi(String accessToken, String secret) {
        return new TwitterTemplate(getConsumerKey(), getConsumerSecret(), accessToken, secret);
    }

}
			

In the constructor, you should call super, passing up the the consumerKey, secret, and configured OAuth1Template. The OAuth1Template will handle the "OAuth dance" with the provider. It should be configured with the provided consumerKey and consumerSecret, along with the provider-specific requestTokenUrl, authorizeUrl, authenticateUrl, and accessTokenUrl. The authenticateUrl parameter is optional and may be left out if the provider doesn't have an authentication URL that is different than the authorization URL.

As you can see here, OAuth1Template is constructed with Twitter's authentication URL (used for provider sign-in; see Chapter 5, Signing in with Service Provider Accounts), which is distinct from their authorization URL. Some providers don't have separate URLs for authentication and authorization. In those cases, you can use OAuth1Template's other constructor which doesn't take the authentication URL as a parameter. For example, here's how the TwitterServiceProvider constructor would look without configuring the authentication URL:

public TwitterServiceProvider(String consumerKey, String consumerSecret) {
    super(consumerKey, consumerSecret, new OAuth1Template(consumerKey, consumerSecret, 
        "https://twitter.com/oauth/request_token",
        "https://twitter.com/oauth/authorize",
        "https://twitter.com/oauth/access_token"));
}
			

In getApi(String, String), you should construct your API implementation, passing it the four tokens needed to make authorized requests for protected resources.

Consult the JavaDoc API of the various service provider types for more information and subclassing options.

3.5 Creating an ApiAdapter

As discussed in the previous chapter, one of the roles of a Connection is to provide a common abstraction for a linked user account that is applied across all service providers. The role of the ApiAdapter is to map a provider's native API interface onto this uniform Connection model. A connection delegates to its adapter to perform operations such as testing the validity of its API credentials, setting metadata values, fetching a user profile, and updating user status:

public interface ApiAdapter<A> {

    boolean test(A api);
			
    void setConnectionValues(A api, ConnectionValues values);

    UserProfile fetchUserProfile(A api);
	
    void updateStatus(A api, String message);
	
}
			

Consider org.springframework.social.twitter.connect.TwitterAdapter as an example implementation:

public class TwitterAdapter implements ApiAdapter<Twitter> {

    public boolean test(Twitter twitter) {
        try {
            twitter.userOperations().getUserProfile();
            return true;
        } catch (ApiException e) {
            return false;
        }
    }

    public void setConnectionValues(Twitter twitter, ConnectionValues values) {
        TwitterProfile profile = twitter.userOperations().getUserProfile();
        values.setProviderUserId(Long.toString(profile.getId()));
        values.setDisplayName("@" + profile.getScreenName());
        values.setProfileUrl(profile.getProfileUrl());
        values.setImageUrl(profile.getProfileImageUrl());
    }
    
    public UserProfile fetchUserProfile(Twitter twitter) {
        TwitterProfile profile = twitter.userOperations().getUserProfile();
        return new UserProfileBuilder().setName(profile.getName()).setUsername(
            profile.getScreenName()).build();
    }
    
    public void updateStatus(Twitter twitter, String message) {
        twitter.timelineOperations().updateStatus(message);	
    }
	
}
			

As you can see, test(...) returns true if the API instance is functional and false if it is not. setConnectionValues(...) sets the connection's providerUserId, displayName, profileUrl, and imageUrl properties from TwitterProfile data. fetchUserProfile(...) maps a TwitterProfile onto the normalized UserProfile model. updateStatus(...) update's the user's Twitter status. Consult the JavaDoc for ApiAdapter and Connection for more information and implementation guidance. We also recommend reviewing the other ApiAdapter implementations for additional examples.

3.6 Creating a ConnectionFactory

By now, you should have an API binding to the provider's API, a ServiceProvider<A> implementation for conducting the "authorization dance", and an ApiAdapter<A> implementation for mapping onto the uniform Connection model. The last step in adding support for a new service provider is to create a ConnectionFactory that wraps up these artifacts and provides a simple interface for establishing Connections. After this is done, you may use your connection factory directly, or you may add it to a registry where it can be used by the framework to establish connections in a dynamic, self-service manner.

Like a ServiceProvider<A>, a ConnectionFactory specialization exists for each authorization protocol. For example, if you are adding support for a OAuth2-based provider, you would extend from OAuth2ConnectionFactory. Implementation guidelines for each type are provided below.

3.6.1 OAuth2

Create a subclass of OAuth2ConnectionFactory<A> named {ProviderId}ConnectionFactory and parameterize A to be the Java binding to the service provider's API. Define a single constructor that accepts a clientId and clientSecret. Within the constructor call super, passing up the assigned providerId, a new {ProviderId}ServiceProvider instance configured with the clientId/clientSecret, and a new {Provider}Adapter instance.

See org.springframework.social.facebook.connect.FacebookConnectionFactory as an example OAuth2ConnectionFactory:

public class FacebookConnectionFactory extends OAuth2ConnectionFactory<Facebook> {
    public FacebookConnectionFactory(String clientId, String clientSecret) {
        super("facebook", new FacebookServiceProvider(clientId, clientSecret), new FacebookAdapter());
    }
}
			

3.6.2 OAuth1

Create a subclass of OAuth1ConnectionFactory<A> named {ProviderId}ConnectionFactory and parameterize A to be the Java binding to the service provider's API. Define a single constructor that accepts a consumerKey and consumerSecret. Within the constructor call super, passing up the assigned providerId, a new {ProviderId}ServiceProvider instance configured with the consumerKey/consumerSecret, and a new {Provider}Adapter instance.

See org.springframework.social.twitter.connect.TwitterConnectionFactory as an example OAuth1ConnectionFactory:

public class TwitterConnectionFactory extends OAuth1ConnectionFactory<Facebook> {
    public TwitterConnectionFactory(String consumerKey, String consumerSecret) {
        super("twitter", new TwitterServiceProvider(consumerKey, consumerSecret), new TwitterAdapter());
    }
}
			

Consult the source and JavaDoc API for ConnectionFactory and its subclasses more information, examples, and advanced customization options.