Last updated Feb 19, 2024

Fisheye and Crucible Development : Stage 4 Twitter access configuration

Register the app in Twitter

To access the Twitter Platform, you need to obtain a Consumer Key/Secret pair and an Access Token/Secret pair. Every user who wants his commits to be tweeted needs to do this.

Go to https://apps.twitter.com/ and select Create new app. Once done, open the Keys and Access Tokens tab, then click Create my access token.

Debug: check Twitter access

Once you have the 4 authentication strings, you might want to check if they are correct and if they truly let you post to Twitter. You can do so with this debug code:

TwitterStatusUpdater.java

1
2
import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.conf.PropertyConfiguration;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Properties;

/**
 * Little app to ensure authorisation works and you can publish tweets to Twitter.
 * First use {@link TwitterTokenGenerator} to obtain access token/secret pair. Then provide this script with the consumer
 * key/secret and access token/secret. Lastly, type in the twitter message you want to post.
 */
public class TwitterStatusUpdater {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        // Consumer key + consumer secret
        print("Enter consumer key:");
        String consumerKey = br.readLine();
        print("Enter consumer secret:");
        String consumerSecret = br.readLine();

        // Access token + access token secret
        print("Enter access token:");
        String token = br.readLine();
        print("Enter access token secret:");
        String tokenSecret = br.readLine();

        TwitterFactory twitterFactory = new TwitterFactory(new PropertyConfiguration(new Properties() {{
            put("oauth.consumerKey", consumerKey);
            put("oauth.consumerSecret", consumerSecret);
            put("oauth.accessToken", token);
            put("oauth.accessTokenSecret", tokenSecret);
        }}));

        Twitter twitter = twitterFactory.getInstance();
        twitter.verifyCredentials();

        print("Enter your Twitter status update:");
        String twitterUpdate = br.readLine();
        twitter.updateStatus(twitterUpdate);
        print("No exceptions, so your update seems successful");
    }

    private static void print(String x) {
        System.out.println(x);
    }
}

Bean of Twitter data

We'd like to store the 4 authentication strings as one object:

TwitterLoginRecord.java

1
2
public class TwitterLoginRecord {

    private final String consumerKey;
    private final String consumerSecret;
    private final String token;
    private final String tokenSecret;

    public TwitterLoginRecord(String consumerKey,
                              String consumerSecret,
                              String token,
                              String tokenSecret) {
        this.consumerKey = consumerKey;
        this.consumerSecret = consumerSecret;
        this.token = token;
        this.tokenSecret = tokenSecret;
    }

    public String getConsumerKey() {
        return consumerKey;
    }

    public String getConsumerSecret() {
        return consumerSecret;
    }

    public String getTokenSecret() {
        return tokenSecret;
    }

    public String getToken() {
        return token;
    }
}

Servlet for Twitter data input

We need to change the "Hello world!" servlet to become a form for typing in the data that will become TwitterLoginRecord. First declare the variable-class mapping on the first line of the template:

twitterSettings.vm

1
2
#* @vtlvariable name="loginRecord" type="com.example.ampstutorial.TwitterAccessTokenRecord" *#

Then change the body to become a form:

twitterSettings.vm

1
2
<body>
<div>
    <form action="./twitter-settings" method="post">
        <table class="dialog-prefs" cellspacing="0">
            <thead>
            <tr>
                <th colspan="2"><h3>Twitter Settings</h3></th>
            </tr>
            </thead>
            <tbody>
            <tr>
                <td class="tdLabel"><label for="consumerKey" class="label">Consumer key:</label></td>
                <td><input type="text" name="consumerKey" value="${loginRecord.consumerKey}" id="consumerKey"/></td>
            </tr>
            <tr>
                <td class="tdLabel"><label for="consumerSecret" class="label">Consumer secret:</label></td>
                <td><input type="password" name="consumerSecret" value="${loginRecord.consumerSecret}" id="consumerSecret"/></td>
            </tr>
            <tr>
                <td class="tdLabel"><label for="token" class="label">Token:</label></td>
                <td><input type="text" name="token" value="${loginRecord.token}" id="token"/></td>
            </tr>
            <tr>
                <td class="tdLabel"><label for="tokenSecret" class="label">Token secret:</label></td>
                <td><input type="password" name="tokenSecret" value="${loginRecord.tokenSecret}" id="tokenSecret"/></td>
            </tr>
            <tr>
                <td></td>
                <td class="action"><input type="submit" value="Save"/></td>
            </tr>
            </tbody>
        </table>
    </form>
</div>
</body>

Note that action="./token-servlet" specifies where the form will post the collected data.

Save data from the servlet post

To accept the data posted from the form, override the doPost method:

TwitterSettingsServlet.java

1
2
import static com.google.common.base.Strings.isNullOrEmpty;
 
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String consumerKey = request.getParameter("consumerKey");
        String consumerSecret = request.getParameter("consumerSecret");
        String token = request.getParameter("token");
        String tokenSecret = request.getParameter("tokenSecret");

        if (       !isNullOrEmpty(consumerKey)
                && !isNullOrEmpty(consumerSecret)
                && !isNullOrEmpty(token)
                && !isNullOrEmpty(tokenSecret)) {
            storeLoginRecord(consumerKey, consumerSecret, token, tokenSecret);
        }
        response.sendRedirect("./twitter-settings");
    }

Note how the parameter names match those in the twitterSettings.vm form.

Store TwitterLoginRecords

For the purposes of this tutorial, the username and password are just stored in a  Map  in a field of the servlet - this is not persistent. To learn how to save configuration data for your plugins see  Storing Plugin Settings for Fisheye .

We'll use TwitterLoginRecordStore interface, so that we can easily switch to a persistent store in the future.

TwitterLoginRecordStore.java

1
2
public interface TwitterLoginRecordStore {

    TwitterLoginRecord get(String userName);
    
    void put(String userName, TwitterLoginRecord record);
}

TwitterLoginRecordStoreImpl.java

1
2
import java.util.HashMap;
import java.util.Map;

public class TwitterLoginRecordStoreImpl implements TwitterLoginRecordStore {

    private final Map<String, TwitterLoginRecord> store = new HashMap<>();

    @Override
    public TwitterLoginRecord get(String userName) {
        return store.get(userName);
    }

    @Override
    public void put(String userName, TwitterLoginRecord record) {
        store.put(userName, record);
    }
}

Add the TwitterLoginRecordStore component

atlassian-plugin.xml

1
2
<component key="twitterLoginRecordStore" class="com.example.ampstutorial.TwitterLoginRecordStoreImpl" public="true">
    <interface>com.example.ampstutorial.TwitterLoginRecordStore</interface>
</component>

Inject and use TwitterLoginRecordStore

TwitterSettingsServlet.java

1
2
private final TwitterLoginRecordStore twitterLoginRecordStore;
private final TemplateRenderer templateRenderer;

public TwitterSettingsServlet(TwitterLoginRecordStore twitterLoginRecordStore, TemplateRenderer templateRenderer) {
    this.twitterLoginRecordStore = twitterLoginRecordStore;
    this.templateRenderer = templateRenderer;
}
 
private void storeLoginRecord(String consumerKey, String consumerSecret, String token, String tokenSecret) {
    twitterLoginRecordStore.put(getCurrentUser(),
    new TwitterLoginRecord(consumerKey, consumerSecret, token, tokenSecret));
}

Get the current user login

We use the user login as the key in the store. To get the current user login, we use EffectiveUserProvider. Add a new dependency:

pom.xml

1
2
<dependency>
    <groupId>com.atlassian.fisheye</groupId>
    <artifactId>fisheye-jar</artifactId>
    <version>3.10.0-SNAPSHOT</version>
    <scope>provided</scope>
</dependency>

Inject the user provider to the servlet and use it:

TwitterSettingsServlet.java

1
2
import com.atlassian.fecru.user.EffectiveUserProvider;

    private final EffectiveUserProvider effectiveUserProvider;

    public TwitterSettingsServlet(EffectiveUserProvider effectiveUserProvider,
                                  TwitterLoginRecordStore twitterLoginRecordStore,
                                  TemplateRenderer templateRenderer) {
        this.effectiveUserProvider = effectiveUserProvider;
        this.twitterLoginRecordStore = twitterLoginRecordStore;
        this.templateRenderer = templateRenderer;
    }
 
    private String getCurrentUser() {
        return effectiveUserProvider.getEffectiveUserLogin().getUserName();
    }

Load TwitterLoginRecord to fill the form

Once saved, we want to show the data in the form. To fill the form template, we need to modify the get method:

TwitterSettingsServlet.java

1
2
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setAttribute("decorator", "fisheye.userprofile.tab");
    response.setContentType("text/html");

    TwitterLoginRecord loginRecord = twitterLoginRecordStore.get(getCurrentUser());
            
    templateRenderer.render("/templates/twitterSettings.vm",
        loginRecord != null ? ImmutableMap.of("loginRecord", loginRecord) : ImmutableMap.of(),
        response.getWriter());
}

Rate this page: