Dear Java, I hate you – accessing the Facebook API with Java

March 30, 2010


So, I’m pretty open minded when it comes to technology, especially diving head-first into programming languages but for some reason I’ve always had a personal vendetta and hatred towards Java even though I’ve never touched the language. I think it comes from my line-of-work; clients ringing up complaining that this Java plugin isn’t working or their browser crashes because of a Java applet. Anyhow, vendettas aside I decided to dive in – fueled with wine gums and semi-naked…

The main reason for playing around with Java was because it’s an upcoming unit in the honours degree I’m studying but also the DLNA server I use, PS3 Media Server (PMS), for streaming movies to my PS3 implemented third-party plugin support – hello!

My aim was to allow users to automatically publish what they are watching/playing on their PlayStation3 via Facebook by creating a PMS plugin.

My first poision of a Java Facebook client was facebook-java-api as it seems to be the most stable, documented and actively developed. After playing around for over a day I couldn’t get a working application, the examples I found around the ‘net were all out of date – functions had been depreciated, Facebook changed it’s API and all sorts of headaches.

38 (thirty-eight!) imports later I had a working application and when I played a movie on my PlayStation it was published on my Facebook wall.

As I dove into the Facebook Java API more and more I decided I didn’t like it. It was crude, heavy, un-logical – something just wasn’t right. For example to simply get a list of friends I had to write 25-or-so lines of code, create a SECOND Facebook object but this time using the Jaxb client which, guess what, equaled more imports to handle Jaxb.

My bitching aside, I looked around a little more and came across RestFB. RestFB’s goals? Simple:

  • Minimal public API
  • Maximal extensibility
  • Robustness in the face of frequent Facebook API changes
  • Simple metadata-driven configuration
  • A single dependency, Apache Log4j

Exactly what I’m looking for, perfect! I found RestFB a dream to work with putting aside my diminishing Java hatred. To get to the point below is some code to retrieve the user ID of the currently logged in user.

// I've removed the imports to keep the size down

public class Main {
	// define our strings
	private static final String FACEBOOK_API_KEY = "xxxxx";
	private static final String FACEBOOK_APPLICATION_SECRET = "xxxxx";
	private FacebookClient facebookClient;
	private String facebookAuthToken = null;
	private String facebookSessionKey = null;

	public static void main(String[] args) throws FacebookException {
		facebookClient = new DefaultFacebookClient(FACEBOOK_API_KEY, FACEBOOK_APPLICATION_SECRET);

		// I always call facebookAuthorise
		// First time users would click a button that calls facebookConnect() first to grab the session key.
		facebookAuthorise();
	}

	private void facebookConnect() throws FacebookException {
		// auth.createToken returns a string
		// http://wiki.developers.facebook.com/index.php/Auth.createToken
		facebookAuthToken = facebookClient.execute("auth.createToken", String.class);
		String url = "http://www.facebook.com/login.php" 
			+ "?api_key=" + FACEBOOK_API_KEY
			+ "&fbconnect=true" + "&v=1.0"
			+ "&connect_display=page" + "&session_key_only=true"
			+ "&req_perms=read_stream,publish_stream,offline_access"
			+ "&auth_token=" + facebookAuthToken;

		// Here we launch a browser with the above URL so the user can login to Facebook, grant our requested permissions and send our token for pickup later
		if (Desktop.isDesktopSupported()) {
			Desktop desktop = Desktop.getDesktop();

			if (desktop.isSupported(Desktop.Action.BROWSE)) {
				try {
					desktop.browse(new URI(url));
				}
				catch(IOException ioe) {
					ioe.printStackTrace();
				}
				catch(URISyntaxException use) {
					use.printStackTrace();
				}
			}
		}
	}

	private void facebookAuthorise() throws FacebookException {
		// I saved my session key to a .properties file for future use so the user doesn't keep have to authorising my app!
		// auth.getSession returns JSON so we need to decode it just to grab the session key
		// http://wiki.developers.facebook.com/index.php/Auth.getSession
		// Here we pickup the session key and define the authToken we used above in facebookConnect()
		JSONObject json = new JSONObject(facebookClient.execute("auth.getSession", String.class, Parameter.with("auth_token", facebookAuthToken)));
		facebookSessionKey = json.getString("session_key");

		// An example call, you can literally use anything from the REST API
		Long uid = facebookClient.execute("users.getLoggedInUser", facebookSessionKey, Long.class);
		System.out.println("FB User ID: " + uid);
	}
}

So, excluding the methods to get a session we managed to use one line of code, simples!

I still hate Java; it complains all the time, it needs dependency after dependency and it seems a little restrictive (like a women). I guess I’ll have to learn to get along with it. AS for the plugin, I’m still developing it and hope to publish it to the PMS community ASAP :)

Tags: , , , ,

39 Comments

  1. Traroth says:

    I don’t really understand here what’s the matter with Java.You seem to have a problem with a Facebook API, but that’s third-party code…

    • Paul Price says:

      Traroth, you’re right. I tried to keep this post mostly Facebook API orientated to keep away the Java fan boys because I know they’re brutal – heh.I think, as I mentioned, the reason I dislike Java is from end-user experience rather then development. I’ve never, ever touched Java before and coming from a PHP background I just found things to be a little crude. For example, adding an import to handle most basic things such as Arrays – I can see this is a benefit in some ways as there’s no point loading the lib into memory if you’re not going to use it.Anyhow, I guess it’s a learning curve and the more I use it the more I’m liking it so we’ll see.. ;)

      • Traroth says:

        Hi Paul,Yeah, if by end-user experience, you mean “applets”, even if I could be considered as a “Java fan-boy”, I’m ok to say it’s simply awful (at least for the century long loading times), but with Swing, it’s possible to make really great GUIs. Check out the new default look&feel, Nimbus, and the skinnable Synth look&feel…

        • funny_with_java says:

          I am tired of people bringing up applets. NO ONE uses them and NO ONE likes them. They were cool in 1997 because it was the only way to have any sort of dynamic app in a browser. I hate it when people bring this old shitty API up that NO ONE uses anymore.

      • max says:

        i think its more of the fb api problem than java itself..lol,if u r from php background(just a scripting lang),u still need to know a powerful system level programming language like .net,java or python.u simply cant hate java for another api mistake .:-)

  2. Hass says:

    Hey PaulFirst of all, thank u so much for this info.And, you’re right about “the examples I found around the ‘net were all out of date – functions had been depreciated”…im having problem with Java too but i need to get it over it since i need to get my project done..here are my question to you rdg your codes1. the FacebookClient : is that a class or from the API itself coz in the API, it has FacebookJsonRestClient, FacebookJaxbRestClient and FacebookXMLRestClient?2. the DefaultFacebookClient : can you explain abt this too.do need your help to survive Java ;) Thank you again

    • Paul Price says:

      Hey Hass, I feel your pain! It seems ever since Facebook dropped official support for their Java API it’s been downhill.The code examples use RestFB (http://restfb.com) which is a third-party library, just like Facebook-Java-API but I found it easier to use.I’d recommend visiting the RestFB website and just having a little play with the code, it really is simple and in my opinion wipes the floor with Facebook-Java-API.I’d be glad to help! :D

  3. Traroth says:

    I tried your code, and instead of the Facebook login page (for the browser part), I get a Facebook page with “An invalid next parameter was specified.” and an com.restfb.FacebookResponseStatusException saying “Received Facebook error response (code 100): Invalid parameter”. Do you have any clue?

    • Paul Price says:

      Hi Traroth, usually “Invalid parameter” means that the you haven’t created an Auth Token via auth.createToken and passed it in the login URL (&auth_token) – could you share your code and I’ll take a look for you…

      • Traroth says:

        Ok, here is my code. It’s actually only a dummy app, just to see how things work. I slightly modified yours:public final static void main(String [] args) {String sessionKey = null;FacebookClient facebookClient = new DefaultFacebookClient(MY_API_KEY, MY_SECRET_KEY);String facebookAuthToken = null;try {facebookAuthToken = facebookClient.execute(“auth.createToken”, String.class);System.out.println(“facebookAuthToken : “+facebookAuthToken);if (FIRST) {String url = “http://www.facebook.com/login.php”+ “?api_key=” + MY_API_KEY+ “&fbconnect=true” + “&v=1.0″//+ “&return_session=true”+ “&connect_display=page” + “&session_key_only=true”+ “&req_perms=read_stream,publish_stream,offline_access”+ “&auth_token=” + facebookAuthToken;System.out.println(“step 1″);if (Desktop.isDesktopSupported()) {Desktop desktop = Desktop.getDesktop();if (desktop.isSupported(Desktop.Action.BROWSE)) {try {desktop.browse(new URI(url));}catch(IOException ioe) {ioe.printStackTrace();}catch(URISyntaxException use) {use.printStackTrace();}}}System.out.println(“step 2″);JSONObject json = new JSONObject(facebookClient.execute(“auth.getSession”, String.class, Parameter.with(“auth_token”, facebookAuthToken)));System.out.println(“step 3″);sessionKey = json.getString(“session_key”);System.out.println(“sessionKey : “+sessionKey);} else {JSONObject json = new JSONObject(facebookClient.execute(“auth.getSession”, String.class, Parameter.with(“auth_token”, facebookAuthToken)));sessionKey = json.getString(“session_key”);Long uid = facebookClient.execute(“users.getLoggedInUser”, sessionKey, Long.class);System.out.println(“uid : “+uid);}} catch (FacebookResponseStatusException e) {System.err.println(“code : “+e.getErrorCode()+” message : “+e.getErrorMessage());e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}static final public boolean FIRST = true;

        • Traroth says:

          One more thing: my code is stopping a between step 2 and step 3, and I get the folowing stack:com.restfb.FacebookResponseStatusException: Received Facebook error response (code 100): Invalid parameterat com.restfb.DefaultFacebookClient.throwFacebookResponseStatusExceptionIfNecessary(DefaultFacebookClient.java:357)at com.restfb.DefaultFacebookClient.makeRequest(DefaultFacebookClient.java:320)at com.restfb.DefaultFacebookClient.execute(DefaultFacebookClient.java:188)at com.restfb.DefaultFacebookClient.execute(DefaultFacebookClient.java:178)at com.kizoa.facebook.FBTester.main(FBTester.java:54)

        • Paul Price says:

          Hmmm, I think I found the problem. If you take a look afterSystem.out.println(“step 2″);you pretty much request the infinitie session key straight away, not giving time for the user to login to Facebook and grant permissions to your application. For example, your application flow is as so;1. Request an AUTH TOKEN from Facebooks server2. Pass the AUTH TOKEN to /login.php and open in a browser for the user to login/request permissions manually and generate a SESSION KEY3. Straight away we attempt to get the SESSION KEY created from step 2 but because we haven’t gave the user enough time to login and generate a session key the Facebook server sends the “Invalid parameter” response.So, what we would need to change is just give time for a user to login to facebook. In my example I open the URL in the browser and then WAIT for the user to push a button which then triggers my auth.getSession()I hope this make sense! :-)

          • Traroth says:

            Yeah, I did this in the meantime. I don’t get the exception anymore. But I still have the message on Facebook.

          • Traroth says:

            Actually I added the following line to my parameters :+ “&next=http://www.facebook.com/connect/login_success.html”and now, it works.

          • Paul Price says:

            Glad you got it working. It’s strange that you have to include NEXT in the URI for it to work.

        • Elizabeth says:

          Thanks Matt, It’s pretty frstirautng I’m on a beach in Thailand, yet I’m thinking about Google Analytics and how I can get things working. Mind you, I’ve just created a flip video message showing the sand and the sea which I will post on my about me page as a motivation message to myself and others to make this all work.Speak soon,John

  4. Raibaz says:

    Hi, i’ve been struggling with the Facebook API to get a session key to use with RestFB all day, then i found your post and it saved my day…thnks a lot! :)

  5. bruno says:

    Cool tip ;) worked perfectly with me. But you tell me how do I get the AUTH_TOKEN user with the login and password it?

    • Paul Price says:

      Hi Bruno, if I read your comment right you want to get the Facebook username after sending your AUTH_TOKEN but before you call auth.getSession? Unfortunately you can’t do this and you need to be authenticated..

  6. Yes, I understand I need to be logged in to facebook to post a photo, but I have some way to authenticate the user at all on facebook backend?Example:I get the login and password and submit to facebook and get the auth_token to configure my application and send the photo.In the API eg twitpic this is possible with just calling a method, but on facebook I need the user to allow my application facebook collect the data from it to post the photo. What I was wondering is if it is somehow easier for me to do this in the backend (background) with no views?

  7. Jagdeep says:

    This is a great link to start with the facebook apis.I want to ligin to a facebook and save the Wall/Home page as a image on my machine. I need to refresh the image every 5 mins so I dont need any windoe prompt.Someone pleas help.

  8. maling says:

    Your code doesn;t work

  9. saqs says:

    Would be nice if U could update this ;)

  10. guna says:

    Hi PaulThank u so much for this info.here are my question to you rdg your codes1. is it possible getting friends facebook account?2. if we get friends list, is it possible to get friends phone number and email?do need your help to survive androidThank you again and regrardssekar

  11. summu says:

    Hi Paul,Do u have any idea regarding this exception “Managing advertisements requires the extended permission ads_management”

  12. Chetan6033 says:

    i was able to connect to the fb by using the above code but i need to retrieve my friend’s(user’s on fb) comments. Is this possible by my application??

  13. matthias says:

    Hello Paul,it seems that “facebookClient.execute” does not exist anymore in the latest version of the restfb library.How can I make the example working with the newest version of restfb ?Thanks very much in advance!BR,Matthias

  14. Craig says:

    Great article Paul. Best example of doing this I have been able to find.Funnily enough, I did the same thing in C# in about 5 minutes, but Java on the other hand….phew, but like you I am about to take a Java course and was determined to do it this way.

  15. Kumar Pallav says:

    Dear Paul,Greetings !!!I don’t find it mature to write content with a heading which brings down the grace of #1 programming language and that too when you are not quite in to it ,to be pretty honest when your knowledge seems even pretty limited about the language, moreover its pretty strange to see the approach to get a access_token. Why to go and and try this much to get an access token which makes you write this much of codes, when it’s just few lines without any dependency and knowing the fact that all you need in response is one line access_tokenbelow are few lines of codeString fbAccessCodeUrl = “https://graph.facebook.com/oauth/access_token?client_id=XXXXXXXXX&redirect_uri=http://localhost:8084/LetsDate/HelloWorld.do&client_secret=XXXXXXX&code=” + code;URL oracle = new URL(fbAccessCodeUrl);URLConnection yc =oracle.openConnection();BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream()));String acessTokenWithExpiryTime;while ((acessTokenWithExpiryTime = in.readLine()) != null) {System.out.println(acessTokenWithExpiryTime);in.close();}I hope we all know which code is reqd to get access_token which is mentioned above :)

    • eth0 says:

      Thanks for your comment. Looking back at this article I cringe. Obviously a lot has changed with the Facebook API since I made this post so I’ll take a look at updating it ASAP.

  16. Naveen says:

    Dear Paul,I have developped one sample app using RestFB API.Deployed in Google App Engine and created an application in FB using the deployed id. While accesing the app, I am getting the following exception.java.lang.NoClassDefFoundError: com/restfb/FacebookClientat java.lang.Class.getDeclaredConstructors0(Native Method)at java.lang.Class.privateGetDeclaredConstructors(Class.java:2406)at java.lang.Class.getConstructor0(Class.java:2716)at java.lang.Class.newInstance0(Class.java:343)at java.lang.Class.newInstance(Class.java:325)at org.mortbay.jetty.servlet.Holder.newInstance(Holder.java:153)at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:428)at org.mortbay.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:339)at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:102)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:249)at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)at org.mortbay.jetty.Server.handle(Server.java:326)at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:135)at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:477)at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:449)at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:455)at com.google.tracing.TraceContext.runInContext(TraceContext.java:695)at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:333)at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:325)at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:453)at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)at java.lang.Thread.run(Thread.java:679)Caused by: java.lang.ClassNotFoundException: com.restfb.FacebookClientat com.google.appengine.runtime.Request.process-3e08f1749a9f64d7(Request.java)at java.lang.ClassLoader.loadClass(ClassLoader.java:266)at java.lang.Class.getDeclaredConstructors0(Native Method)at java.lang.Class.privateGetDeclaredConstructors(Class.java:2406)at java.lang.Class.getConstructor0(Class.java:2716)at java.lang.Class.newInstance0(Class.java:343)at java.lang.Class.newInstance(Class.java:325)at org.mortbay.jetty.servlet.Holder.newInstance(Holder.java:153)at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:428)at org.mortbay.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:339)at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)at org.mortbay.jetty.Server.handle(Server.java:326)at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:449)at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:455)at com.google.tracing.TraceContext.runInContext(TraceContext.java:695)at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:333)at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:325)at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:453)… 1 morePlease advice me to resolve this exception.Thanks In Advance !!!

    • eth0 says:

      Hi Naveen, it looks like your missing a file or a import. Look at the first line, it seems to be missing “FacebookClient”.

  17. Jan Cajthaml says:

    Don’t hate Java, hate shitty code.And don’t mistake Java for Applet.Its like mistaking XSLT for HTML of XPath for DOM.I hate C/C++ but not for the language but for the people.They thing C/C++ is only language that has pointer,destructors,phantom references etc. (Java has it with less memory and CPU consumtion)So please Don’t complain about programming language, complain about lazy coders.

  18. Traroth says:

    One more thing: my code is stopping a between step 2 and step 3, and I get the folowing stack:com.restfb.FacebookResponseStatusException: Received Facebook error response (code 100): Invalid parameterat com.restfb.DefaultFacebookClient.throwFacebookResponseStatusExceptionIfNecessary(DefaultFacebookClient.java:357)at com.restfb.DefaultFacebookClient.makeRequest(DefaultFacebookClient.java:320)at com.restfb.DefaultFacebookClient.execute(DefaultFacebookClient.java:188)at com.restfb.DefaultFacebookClient.execute(DefaultFacebookClient.java:178)at com.kizoa.facebook.FBTester.main(FBTester.java:54)

  19. Traroth says:

    Yeah, I did this in the meantime. I don’t get the exception anymore. But I still have the message on Facebook.

  20. Traroth says:

    Actually I added the following line to my parameters :+ “&next=http://www.facebook.com/connect/login_success.html”and now, it works.

  21. max says:

    i think its more of the fb api problem than java itself..lol,if u r from php background(just a scripting lang),u still need to know a powerful system level programming language like .net,java or python.u simply cant hate java for another api mistake .:-)

Leave a Comment