Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
matt_steiner
Active Contributor

In this third chapter of this blog series originating from our SAP TechEd session Tapping into Open Source with SAP NetWeaver Cloud we'll talk about: social media integration.

With the ascent of social networks such as Facebook, Twitter or LinkedIn the power of consumers has been steadily growing. Companies big and small can no longer ignore the opinion of consumers (social buyers) and are well-advised to listen on such channels and ultimately to participate  in the conversation (to positively influence it.) One of the resulting trends is sentiment analysis (see How Companies Can Use Sentiment Analysis to Improve Their Business) leading to a whole new understanding of marketing called pull marketing: Using Social Media for Building, Selling and Supporting.

"Instead of holding a megaphone up to our mouth, we should be holding the megaphone up to our ears with the little end toward our ears and the big end facing out to our marketplace, listening to what they need from us. And the tone with which we provide it has to change, so that instead of being pushy and insistent on getting peoples’ attention, we should be sharing information openly in a more conversational and helpful way."

[mark.yolton in an interview with MITSloan Management Review]

Ultimately, the best form of marketing is to have happy customers recommending your products proactively to their friends, colleagues and peers. So, it sounds like a good idea to let users do that as simply as possible - directly from within your web page, platform or application.

So, whether it comes to integration content from social networks or allowing users to share content: today's application are well-advised to embrace social media. As such, let us have a look on how-to integrate Twitter into our application for example.

As you may recall our session supplement web application features a Twitter dashboard that shows the latest tweets containing the "sapnwcloud" hashtag as well as the most active tweeps recently. In chapter 2, lars.karg explained how we developed the UI to display this data. Now, we'll see how we developed the backend functionality. Off we go...

1. Getting started

This tutorial continues where chapter 1 left off, so if you want to code along, please clone the following repo: https://github.com/sapnwcloud/springrest

2. Create new spring configuration

First, let's create a new spring configuration within the webapp/WEB-INF directory called: springsocial-context.xml

It does look very similar to the old one, but there are a few important differences:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"  
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
       http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
       http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
    <context:component-scan base-package="com.sap.netweaver.cloud.samples.springsocial"/>
<bean id="baseService" class="com.sap.netweaver.cloud.samples.springsocial.srv.BaseService" abstract="true">
</bean>
<bean id="dashboardService" class="com.sap.netweaver.cloud.samples.springsocial.srv.DashboardService" parent="baseService"/>
<bean id="tweetService" class="com.sap.netweaver.cloud.samples.springsocial.srv.TweetService" parent="baseService"/>
<jaxrs:server id="restContainer" address="/v1">
  <jaxrs:properties>
            <entry key="org.apache.cxf.propagate.exception" value="false" />
        </jaxrs:properties>
  <jaxrs:serviceBeans>
  <ref bean="dashboardService"/>
  <ref bean="tweetService"/>
  </jaxrs:serviceBeans>
  <jaxrs:providers>
            <ref bean="jacksonProvider" />
            <ref bean="formProvider" />
        </jaxrs:providers>
</jaxrs:server>
<bean id="jacksonProvider" class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider">
  <property name="mapper" ref="objectMapper"/>
</bean>
<bean id="formProvider" class="org.apache.cxf.jaxrs.provider.FormEncodingProvider" />
<bean id="objectMapper" class="com.sap.netweaver.cloud.samples.springsocial.srv.CustomJsonMapper" />
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations" value="classpath:oauth.properties"/>
</bean>
<bean id="twitter" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
  <constructor-arg index="0" value="${oauth.consumerKey}"/>
  <constructor-arg index="1" value="${oauth.consumerSecret}"/>
  <constructor-arg index="2" value="${oauth.accessToken}"/>
  <constructor-arg index="3" value="${oauth.accessTokenSecret}"/>
</bean>
</beans>

Lines 20-24 define the needed service beans. To show good practices I declare an empty BaseService bean, so that I can add central functionality in a common place later-on. I then define two Servcies: one which is in charge of providing the data for the Twitter Dashboard, the other is used to tweet messages.

Lines 34-37 define so called JAX-RS providers. Simply put, they are needed to (de)-serialize java objects to JSON and back. Jackson is a very cool library for this purpose as it greatly simplifies the process, especially when it comes to list and maps. The FormEncodingProvider is used to handle form data (used to tweet the text message submitted via an HTML form.)

In line 46 a custom JSON mapper is defined. As discussed in chapter 1, the main reason to do this is to have a bit more control of the de/-serialization of JSON.

Line 48-50 declare a so called PropertyPlaceholderConfigurer, which simply makes the values declared in a property file (oauth.properties) accessible within the spring configuration (lines 53-56). The reason for this approach was that we need to declare a few private keys to interact with Twitter, which for obvious reasons we do not want to check-into github.

In lines 52 ff the TwitterTemplate is defined, which is used to interact with the Twitter API. As part of the initialization the oauth parameters are passed (which are obtained form the before mentioned property file.

3. Update the web.xml

The web.xml file is almost identical to the one created for chapter 1.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
   <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/springsocial-context.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
  </listener>
  <servlet>
    <servlet-name>CXFServlet</servlet-name>
    <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/api/*</url-pattern>
  </servlet-mapping>
</web-app>

Of course, the name of the spring configuration was replaced (line 10). We also removed the authentication section again. Last, we slightly change the mapping to the CXF servlet (line 26). Remember, in chapter 1 we used the "*" (match-all) mapping. In this chapter, we also want to display an HTML page that includes the form to submit our twitter status update, hence we cannot use the catch-all mapping no more.

4. Update the pom.xml

First, let's add some new properties again in the <properties /> section:

  <org.codehaus.jackson.version>1.9.9</org.codehaus.jackson.version>
  <org.slf4j.version>1.6.1</org.slf4j.version>
  <org.springframework.social.version>1.0.2.RELEASE</org.springframework.social.version>

Then we need to define some additional dependencies for spring social, jackson, guava (collections) and some logging-related dependencies respectively. Please have a look at the complete pom.xml file for the details.

5. Create domain model

For this application we need three domain model objects: Tweet, User and Dashboard. These objects are mere POJOs and only define the attributes and getter and setter methods. The only thing worth mentioned explicitly are the javax.xml.bind.annotation annotations, which are used to define the name of the properties when serialized into JSON. (These annotations are used by Jackson during the (De-)serialization process.

6. Create the Service classes

We already discussed the services during step 2. Let's have a closer look at them:

DashboardService

package com.sap.netweaver.cloud.samples.springsocial.srv;
// ...
@Service("dashboardService")
@Path("/dashboard")
@Produces({ "application/json" })
public class DashboardService extends BaseService
{
          @Inject
          ObjectMapper mapper = null;
          @Inject
          Twitter twitter = null;
          @GET
          @Path("/{id}")
          public Dashboard getDashboardByID(@PathParam("id") String id)
          {
                    Dashboard dashboard = new Dashboard();
                    SearchResults results = twitter.searchOperations().search("#sapnwcloud");
                    //...
                    return dashboard;
          }
}

I omitted some of the coding for better readability (if you're interested in the details please check out the complete class source code here: DashboardService)

Line 5-7 are the familiar JAX-RS annotation again. We discussed them in detail in chapter 1.

Line 10-11 and 13-14 define references to other services we declared in the spring configuration. Note the Inject annotation, which instructs Spring to inject a reference to the specified bean at runtime (no getters/setters needed!)

Line 18-23 comprise the service operation. Here you can see the Twitter API at work. All we do here is to perform a public search for teh #sapnwcloud hashtag on Twitter. Pretty simple, isn't it?

TweetService

package com.sap.netweaver.cloud.samples.springsocial.srv;
//...
@Service("tweetService")
@Path("/twitter")
@Produces({ "application/json" })
public class TweetService extends BaseService
{
          @Inject
          Twitter twitter = null;
          @Path("/tweet")
          @POST
          public Response tweet(@FormParam("message") String message)
          {
                    try
                    {
                              Tweet tweet = twitter.timelineOperations().updateStatus(message);
                              return Response.ok(tweet).build();
                    }
                    catch (Exception ex)
                    {
                              return Response.serverError().entity(ex.getMessage()).build();
                    }
          }
}

By now, most of the coding should look fairly familiar and require little further explanations. Except for the FormParam annotation maybe... in a nutshell, the method gets the (HTML) form field with the name "message" injected by JAX-RS. (This will get much clearer once we look at the corresponding HTML page in a minute!) We simply take that message and post it as a Twitter status message... that's all.

7. Create the HTML page

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Basecamp</title>
</head>
<body>
<form action="api/v1/twitter/tweet" method="post">
          <textarea name="message" rows="2" cols="80"></textarea><br>
          <input type="submit" class="btn" value="Tweet">
</form>
</body>
</html>

It's a very simple form indeed. Please note the action URL we provide - which maps to the TweetService tweet() method. Furthermore, you can see that the textarea's name is "message", which was the name we referred to within the FormParam annotation in the tweet() method.

8. Provide the required OAuth credentials

The last remaining step is to provide the OAuth credentials required to interact with the Twitter API. Now, those of you who ever tried to perform the so-called "OAuth" dance manually will know that's far from being trivial and as such appreciate the fact that Spring Social take care of all the heavy lifting for us. Just have a look at the digrams on the public help document to get the idea:

http://static.springsource.org/spring-social/docs/1.0.x/reference/html/serviceprovider.html

The Service Provider framework is very powerful actually and I'm barely scratching the surface here. For the sake of keeping things digestible though we kept it simple and hence you only need to provide the four required parameters in the oauth.properties file:

# Please enter your oAuth credentials as provided by dev.twitter.com when you registered your app!
oauth.consumerKey=
oauth.consumerSecret=
oauth.accessToken=
oauth.accessTokenSecret=

You'll get these keys after you successfully registered an application of dev.twitter.com.

Wrap-up

With that you should be all set to start coding social apps. Sure, we only used Twitter in this example, but most of the popular social networks have similar support in Spring Social so it'S definitely worth checking it out! Here's a rudimentary Twitter client I developed a while back - Twitter Demo - and here's the source-code: https://github.com/sapnwcloud/socialspringdemo

Now that you're familiar with the basics of Maven-based Java projects and Spring you may want to take it to the next level and learn how you can create entire projects including persistence, MVC-based frontend and even Gateway connectivity services with just a few lines of scripting using Spring Roo. If so, then jump right into lars.karg 's blog TechEd 2012 CD208 (Chapter 4/6) – Spring Roo