|
TwitterAndSignpost
Real-life example of how to use Signpost with Twitter
Using Signpost with TwitterYou can get a consumer key and secret for your Twitter application here. Simple Example - Setting up authentication and GET'ing mentions OAuthConsumer consumer = new DefaultOAuthConsumer(
// the consumer key of this app (replace this with yours)
"iIlNngv1KdV6XzNYkoLA",
// the consumer secret of this app (replace this with yours)
"exQ94pBpLXFcyttvLoxU2nrktThrlsj580zjYzmoM",
SignatureMethod.HMAC_SHA1);
OAuthProvider provider = new DefaultOAuthProvider(consumer,
"http://twitter.com/oauth/request_token",
"http://twitter.com/oauth/access_token",
"http://twitter.com/oauth/authorize");
/****************************************************
* The following steps should only be performed ONCE
***************************************************/
// we do not support callbacks, thus pass OOB
String authUrl = provider.retrieveRequestToken(OAuth.OUT_OF_BAND);
// bring the user to authUrl, e.g. open a web browser and note the PIN code
// ...
String pinCode = // ... you have to ask this from the user, or obtain it
// from the callback if you didn't do an out of band request
// user must have granted authorization at this point
provider.retrieveAccessToken(pinCode);
// store consumer.getToken() and consumer.getTokenSecret(),
// for the current user, e.g. in a relational database
// or a flat file
// ...
/****************************************************
* The following steps are performed everytime you
* send a request accessing a resource on Twitter
***************************************************/
// if not yet done, load the token and token secret for
// the current user and set them
consumer.setTokenWithSecret(ACCESS_TOKEN, TOKEN_SECRET);
// create a request that requires authentication
URL url = new URL("http://twitter.com/statuses/mentions.xml");
HttpURLConnection request = (HttpURLConnection) url.openConnection();
// sign the request
consumer.sign(request);
// send the request
request.connect();
// response status should be 200 OK
int statusCode = request.getResponseCode();Here is what you typically do step by step:
You can also download this example code as a Java/Eclipse project and go from there. This example application executes all steps above sequentially in the console. If run successfully, its output should be something like this: Fetching request token from Twitter... Request token: g1re4iYfknOYMB62JZkddjwhCfvfn4WPdZrzgscMOA Token secret: wKStK0dmhPcuFxBki3imJv7yVNXgRndmW4LSmuCg Now visit: http://twitter.com/oauth/authorize?oauth_token=g1re4iYfknOYMB62JZkddjwhCfvfn4WPdZrzgscMOA ... and grant this app authorization Enter the PIN code and hit ENTER when you're done: xxxxxxx Fetching access token from Twitter... Access token: 14418463-Xt70aZ8b2jz19MzY7JnQJ6HglPh0Hc5p939gLH5YI Token secret: TkG689FOx7amgdX2ta6epE5MYsmZxVuO9ith7FtJrs Sending request to Twitter... Response: 200 OK Elaborate Example - POST'ing status updatesThanks Kerry for sharing this via our Google group! NOTE: This example assumes that you're using Apache HttpClient for HTTP messaging. We'll need to tweak some of HttpClient's defaults, like so: public HttpParams getParams() {
// Tweak further as needed for your app
HttpParams params = new BasicHttpParams();
// set this to false, or else you'll get an
// Expectation Failed: error
HttpProtocolParams.setUseExpectContinue(params, false);
return params;
}
// Your actual method might look like this:
public void updateProfileBackground(User user, File file) {
try {
// Create a new consumer using the commons implementation
OAuthConsumer consumer = new CommonsHttpOAuthConsumer(consumerKey,
consumerSecret, SignatureMethod.HMAC_SHA1);
consumer.setTokenWithSecret(getUserAccessToken(user),
getUserTokenSecret(user));
HttpPost uploadBackgroundPost = new HttpPost(
UPDATE_PROFILE_BACKGROUND_IMAGE_URL);
// The body of a multi-part post isn't needed
// for the generation of the signature
consumer.sign(uploadBackgroundPost);
// only works in strict mode
MultipartEntity entity = new MultipartEntity(
HttpMultipartMode.STRICT);
// Twitter checks against supported file types
FileBody imageBody = new FileBody(file, "image/png");
entity.addPart("image", imageBody);
uploadBackgroundPost.setEntity(entity);
DefaultHttpClient httpClient = new DefaultHttpClient(getParams());
// If you're interested in the headers,
// implement and add a request interceptor that prints them
httpClient.addRequestInterceptor(new PrintRequestInterceptor());
System.out.println(httpClient.execute(uploadBackgroundPost,
new BasicResponseHandler()));
} catch (Exception e) {
// do some proper exception handling here
e.printStackTrace();
}
}Now oddly enough, if the multi-part form data is not being formatted exactly to Twitter's preferences you won't get an error message, but the background image will be set to a broken link! You might see the image url being constructed as: http://a3.twimg.com/profile_background_images/test.png instead of: http://a3.twimg.com/profile_background_images/5463125/test.png (Notice the missing folder) Instructions for using Apache HttpClient 2.xAdditionally here's an example if you're using Apache HttpClient2.x, which uses a different API than HttpClient 4.x. First off you'll need to make your own adapter for the MultipartPostMethod (or if you're adventurous HttpMethod more generally). Something along the lines of: public class HttpClient2OAuthConsumer extends AbstractOAuthConsumer {
public HttpClient2OAuthConsumer(String consumerKey,
String consumerSecret, SignatureMethod signatureMethod) {
super(consumerKey, consumerSecret, signatureMethod);
}
protected oauth.signpost.http.HttpRequest wrap(Object request) {
if (!(request instanceof MultipartPostMethod))
throw new IllegalArgumentException(
"This consumer expects requests of type MultipartPostMethod");
else
return new HttpRequestAdapter((MultipartPostMethod) request);
}
}And: public class HttpRequestAdapter implements oauth.signpost.http.HttpRequest {
private MultipartPostMethod method;
public HttpRequestAdapter(MultipartPostMethod method) {
this.method = method;
}
public String getContentType() {
Header contentHeader = method.getRequestHeader("Content-Type");
return (contentHeader == null) ? "" : contentHeader.getValue();
}
public String getHeader(String s) {
return method.getRequestHeader(s).getValue();
}
public InputStream getMessagePayload() throws IOException {
return new FileInputStream(method.getFileData());
}
public String getMethod() {
return method.getName();
}
public String getRequestUrl() {
try {
return method.getURI().getURI();
} catch (URIException e) {
e.printStackTrace();
}
return null;
}
public void setHeader(String s, String s1) {
method.setRequestHeader(s, s1);
}
}This method will look very similar. public void updateProfileBackgroundHttpCommons(User user, File file) {
try {
HttpClient client = new HttpClient();
// make a new consumer with our own implementation
OAuthConsumer consumer = new HttpClient2OAuthConsumer(consumerKey,
consumerSecret, SignatureMethod.HMAC_SHA1);
consumer.setTokenWithSecret(getUserAccessToken(user),
getUserTokenSecret(user));
MultipartPostMethod mp = new MultipartPostMethod(
UPDATE_PROFILE_BACKGROUND_IMAGE_URL);
FilePart image = new FilePart("image", file, "image/png", null); // again specify the type
// charset cannot be set to a type.
// httpclient2.x mistakenly adds one to a filepart by default
image.setCharSet(null);
mp.addPart(image);
mp.setStrictMode(false);
consumer.sign(mp);
client.executeMethod(mp);
for (Header header : mp.getRequestHeaders()) {
System.out.println(header.toExternalForm());
}
System.out.println(mp.getResponseBodyAsString());
} catch (Exception e) {
// do some proper exception handling here
e.printStackTrace();
}
}And that's it. |
Sign in to add a comment
authentication fails possibly due to PIN-based process
the method in DefaultOAuthProvider should be modified to accept pin
public void retrieveAccessToken(String pin)
When I run the example code I get:
Exception in thread "main" java.lang.UnsupportedClassVersionError?: Bad version number in .class file
Thanks for the post.
how to retrieve the response from twitter ?
http://hc.apache.org/httpcomponents-client/httpclient/apidocs/org/apache/http/client/HttpClient.html nevermind. check this link for my question above.
The library hasn't been updated to implement OAuth 1.0a, yet, so Twitter and Fire Eagle support is broken.
I'll let you guys know when it's working again.
Matthias, what's the symptom of the breakage? I'm trying this example using 1.1 (well, a close version of it) and I'm seeing:
About the "Exception in thread "main" java.lang.UnsupportedClassVersionError??: Bad version number in .class file" error, it happens when you run the code with java 1.5. You need to use java 1.6
Hey guys,
As to the first question: The 1.1 build has a bug in OAuthProvider where the connection is cached incorrectly, causing the already connected exception. Will fix that asap.
As to the UnsupportedClassVersionError?: The zip file containing the example comes with an old build of Signpost (1.0-SNAPSHOT) which was built using JDK6. I already fixed that (Signpost no longer needs Java 6 to run), but I have to update the example archive to ship with the new build.
I have my hands full with work right now, but I will get to these things asap.
Thanks everyone for reporting!
Do let us know when the fix is in :-)
BTW, just wanted to say how nice it is to use a straightforward OAuth that has good examples! Keep up the good work!
One thing that would be nice is signpost java-twitter integration, but that might be too much to ask, and perhaps I should take a cut at it myself...
Just updated the lib and the examples to work with 1.0a providers.
Thanks again for your input and let me know if something still doesn't work as expected.
how to get data from twitter using accesstoken?
Is there anyway to serialize the OAuthProvider for storage so that the OAuthProvider.retrieveAccessToken(String) step can be executed later in a seperate thread? If not, how can you create a new OAuthProvider object to continue with the retrieveAccessToken step?
Thanks.
Hi,
You could simply re-create the OAuthProvider with an OAuthConsumer that has all the token data set from where you left off, i.e. by doing this:
// Thread 1 provider.retrieveRequestToken(); // consumer now has request token + secret set // ... serialize token and secret
// Thread 2: OAuthConsumer consumer = new DefaultOAuthConsumer(...); consumer.setTokenWithSecret(/deserialized token and secret/); OAuthProvider provider = new DefaultOAuthProvider(consumer, ...); provider.retrieveAccessToken();
I didn't test this, but it should work.
A better solution however would simply be to declare both classes serializable. Both are very simple objects carrying almost no state. The provider holds a URLConnection reference, but that can safely be declared transient.
Can you open a ticket on the issue tracker for this please? I'll look into this in more detail then.
Yes it works. If you could make them serializable it would be perfect. You might want to add a getConsumer() method in the OAuthProvider interface for this to work.
alrighty, will do.
Nice one, works for me also!
OAuthConsumer and OAuthProvider are now serializable.
Cheers, Matthias
Wow, that is quick. Thanks a lot for the good work.
Hi I got a 401 error message. Im pretty sure I'm using my own keys and it doesn't work. What could be wrong?
Fetching request token from Twitter... Exception in thread "main" oauth.signpost.exception.OAuthNotAuthorizedException: Authorization failed (server replied with a 401). This can happen if the consumer key was not correct or the signatures did not match.
Java Result: 1Is Twitter giving me bad keys? I also tried twitter4J and I had No luck.
You will get a 401 if the signatures computed by the client and the server do not match. This can happen for many reasons, but since you say it doesn't work for you anywhere anymore, it's very likely a problem with the set of credentials you're using (wrong key/secret combination maybe?). Cannot say much about this without more information.
Hey it worked my University (a PC with public IP btw)!. HOWEVER, when I come back home, I try running the same code and then it doesn't work again...
I mean I use THE SAME code and I used in the university (same project). I also CHANGED the keys and tried all over again with new keys and nothing! it prompts:
"Exception in thread "main" oauth.signpost.exception.OAuthNotAuthorizedException: Authorization failed (server replied with a 401). This can happen if the consumer key was not correct or the signatures did not match."
Then I remote-accessed to the University's PC, and then I ran the same code and it worked again (with the Access Tokens and even with Request Tokens!!!). It seems that the code compiles in the University but NOT at home. So must I have a Public IP in order to make it work?? I don't find the reason why it won't work at home (I need it to work at home).
I was wondering if it's something about the ports and they're blocked here. Any ideas? :(
That's pretty odd. It's very likely not a network problem. You will see that exception if the server answered with a 401 Unauthorized -- that happens if signature verification failed (client computed a different signature for that request than the server).
I can only guess here... but maybe it's an encoding problem (common among the OAuth camp)? You may want to send this through a debugger and see what you can find out. You may especially want to look at the signature base strings generated on the different clients and check for any peculiarities.
Just curious, at which point does the 401 occur? During token request or while sending the signed request? One idea I have: Are you sure that you entered the PIN code exactly as-is? It's easy to accidentally have leading or trailing white space when copy-n-pasting from a website.
Well, it ocurrs at: String authUrl = provider.retrieveRequestToken(OAuth.OUT_OF_BAND);
it says: oauth.signpost.exception.OAuthNotAuthorizedException
I'm doing this at home, where I guess there's some kind of restriction to my IP (non-static IP). At the university, as I told you, it works just fine. So what could be happening?
Hi,
I got the same issue with jbasurtod.
Fetching request token from Yammer... Request token: 0dBh4lSgf4S8u3xxxxxxx Token secret: CvM6hTZojYZX7jIKjLqvlPZBP7dgD8iw0gjxxxxxxx Now visit: https://www.yammer.com/oauth/authorize?oauth_token=0dBh4lSgf4S8u3xxxxxxx&oauth_callback=oob ... and grant this app authorization Enter the PIN code and hit ENTER when you're done: 59xx Fetching access token from Yammer... Exception in thread "main" oauth.signpost.exception.OAuthNotAuthorizedException: Authorization failed (server replied with a 401). This can happen if the consumer key was not correct or the signatures did not match.
Do you have any idea about this?
Hey, actually, with the same source code, it's working with Twitter.
Fetching request token from Twitter... Request token: kQDVNkKx2qK4CAIihEFHyvgWOkeuHrO8bnPxxxxxxx Token secret: hOFzcKyUSFjs6ZEPjJ2lKlydOxC5c2b00MvWxxxxxxx Now visit: http://twitter.com/oauth/authorize?oauth_token=kQDVNkKx2qK4CAIihEFHyvgWOkeuHrO8bnPxxxxxxx ... and grant this app authorization Enter the PIN code and hit ENTER when you're done: 520xxx Fetching access token from Twitter... Access token: 42808709-LxEKZpgUq6ltPSROMYKfKth7rpamXVd34wxxxxxxx Token secret: ZlgkoVaPqN0xYHabRXXYilcB8zjFjttxxxxxxx Sending request to Twitter... Response: 200 OK
So I really don't know why it's not working with Yammer?
I'm at the final stage where I need to post an update so I do the following and getting 400 (Bad Request). App is running on Android 1.5. What am I doing wrong?
URL url = new URL("http://twitter.com/statuses/update.xml"); HttpURLConnection request = (HttpURLConnection) url.openConnection(); request.addRequestProperty("status", "Hello World!"); // sign the request consumer.sign(request); // send the request request.connect(); // response status should be 200 OK int statusCode = request.getResponseCode();Hi,
I've had a test with OAuth command line Java sample and has the issue with Yammer, not Twitter. You can refer at:
http://code.google.com/p/oauth/issues/detail?id=106&colspec=ID%20Type%20Status%20Priority%20Lib%20Owner%20Summary
Please let's not turn this into a discussion/q&a here, especially when the problem is not related to the Twitter example (as is the case with the yammer stuff). There is a discussion forum for this: http://groups.google.com/group/signpost-users Please open a topic there.
Also, I'll do my best, but please bear in mind that I don't have the time to investigate every single problem.
you need in android mto update the manifest to give a connection bostone can you help me with my code i can't seem to wrap my head around this. I am also using android.
email basketballdeon@gmail.com
Hey guys - I extended your example to show how to do actual Twitter status update. You can find it here
With the latest build, you need to set oAuth10a to true (setOAuth10a(true);)