The Facebook API doesn't allow you directly to send messages. Except that it does.

So how to do this? While looking for xmpp-libraries, I found smack. So I figured, I'd try it out:

<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack</artifactId>
    <version>3.2.1</version>
</dependency>

And a class to test it out:

public class FBChatClient {
    public static final String XMPP_HOST = "chat.facebook.com";
    public static final int    XMPP_PORT = 5222;
    private final String appSecret;
    private final String appKey;

    public FBChatClient(String appKey, String appSecret) {
        this.appKey = appKey;
        this.appSecret = appSecret;
    }

    public void login(String username, String password) throws XMPPException {
        SASLAuthentication.registerSASLMechanism("DIGEST-MD5", SASLDigestMD5Mechanism.class);
        SASLAuthentication.supportSASLMechanism("DIGEST-MD5", 0);

        ConnectionConfiguration config = new ConnectionConfiguration(XMPP_HOST, XMPP_PORT);
        XMPPConnection connection = new XMPPConnection(config);
        config.setSASLAuthenticationEnabled(true);
        connection.connect();
        connection.login(username, password);
    }
}

Now let's test it:

public class FBChatClientTest {

    private static final String apiKey    = ("13112999370xxxx");
    private static final String apiSecret = ("88356495a784f406efd74xxxxx");
    private FBChatClient client;


    @Before
    public void setUp() throws Exception {
        this.client = new FBChatClient(apiKey, apiKey);
    }

    @Test
    public void testLogin() throws Exception {
        client.login(System.getenv("FB_USER"), System.getenv("FB_PASS"));
    }
}

results in

Error! A startup class specified in smack-config.xml could not be loaded: org.jivesoftware.smackx.ServiceDiscoveryManager
Error! A startup class specified in smack-config.xml could not be loaded: org.jivesoftware.smackx.XHTMLManager

Adding smackx solved this:

<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smackx</artifactId>
    <version>3.2.1</version>
</dependency>

But now we get:

XMPPError connecting to chat.facebook.com:5222.: remote-server-error(502) XMPPError connecting to chat.facebook.com:5222.
  -- caused by: java.net.ConnectException: Operation timed out
    at org.jivesoftware.smack.XMPPConnection.connectUsingConfiguration(XMPPConnection.java:524)
    at org.jivesoftware.smack.XMPPConnection.connect(XMPPConnection.java:953)
    at com.company.fbchat.FBChatClient.login(FBChatClient.java:31)
    at com.company.fbchat.FBChatClientTest.testLogin(FBChatClientTest.java:24)

Turn's out, my company is blocking access on this port. So let's setup a proxy:

$ ssh -ND 8888 arc2

Now we got to change the Connection Configuration through:

ConnectionConfiguration config = new ConnectionConfiguration(XMPP_HOST, XMPP_PORT, ProxyInfo.forSocks4Proxy("localhost", 8888, null, null));

Of course, if all would work well would would let it be injected. Now let's check if that changed something:

Feb 21, 2013 11:07:31 AM com.company.fbchat.FBChatClient login
DEBUG: connecting
Feb 21, 2013 11:07:32 AM com.company.fbchat.FBChatClient login
DEBUG: logging in
SASL authentication DIGEST-MD5 failed: not-authorized: 
    at org.jivesoftware.smack.SASLAuthentication.authenticate(SASLAuthentication.java:337)
    at org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:203)
    at org.jivesoftware.smack.Connection.login(Connection.java:348)
    at com.company.fbchat.FBChatClient.login(FBChatClient.java:43)
    at com.company.fbchat.FBChatClientTest.testLogin(FBChatClientTest.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:76)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Now that looks better. However, it seems that we do something wrong with the authentication. Switching to CustomSASLDigestMD5Mechanism (from here) worked. If it didn't, ensure that you got the xmpp permission and check your username. You need to use the FB username (id would work too, I guess), not your email address.

Now to conclude this post, let's send a message:

XMPPConnection connection = new XMPPConnection(config);
LOG.debug("connecting");
connection.connect();
LOG.debug("logging in");
connection.login(username, password);

Collection<RosterEntry> entries = connection.getRoster().getEntries();

Chat chat = connection.getChatManager().createChat("-100000651472xxx@chat.facebook.com", null);
Message message = new Message("-100000651472xxx@chat.facebook.com", Message.Type.chat);
message.setSubject("Subject cool");
message.setBody("Hier mal ein Link: http://google.de");
chat.sendMessage(message);

Pay attention that you prefix the facebook id you want to send to with -. Now this is all still far from ideal since you require the user to enter his password. However, that's as far as we got. We switched platform after that so this code was not needed anymore. But maybe it can help you?