Saturday, March 21, 2015

Version 0.5.0 Release Notes

Yay, after a few months of work, version 0.5.0 has been released and pushed to Maven Central!

This release contains some new features, most noteworthy:

And as always bug fixes, more JavaDoc, minor improvements, micro optimizations etc.

A more comprehensive list can be found in the changelog.

Special thanks goes to Markus Karg, who has provided many good ideas and test results, issued bugs and suggested improvements!

What’s next?

Moving to Java 8

0.5.0 will be the last release for Java 7, future releases will be for Java 8.

The reasons behind this decision are:
  • Java 8 offers so much cool new stuff, which is worth exploring and probably helps improving the API in a sustainable way. I am thinking especially about the Stream API, java.time and java.util.function packages and the new Async API.
  • As of now, this library is developed and tested with Java 8 only, which makes maintaining Java 7 compatibility a burden.

New Features

I only want to give a vague preview here, but my plans for new features are currently to implement XEP-0198: Stream Management and finally finish XEP-0301: In-Band Real Time Text.

Happy coding!

Friday, February 13, 2015

Bringing XMPP Chat States to JavaFX

Chat States. Everybody knows them from Instant Messengers or Facebook Chat. Those tiny notifications, which tell you, if your chat partner is currently composing a message, is (in)active or has paused typing. E.g. "XY is typing..."

XMPP defines these states in XEP-0085: Chat State Notifications as:
  • active
  • inactive
  • gone
  • composing
  • paused

Each of them - except "gone" - are applicable to a "message input interface". So let's translate them to a JavaFX TextArea!

First we define, that whenever the TextArea receives focus, we want to change the state from 'inactive' (which is the initial default state) to 'active' (if there's no text) or to 'paused' (if there's already text):

Secondly we have to change to 'composing', whenever the text changes. Easy.

The slightly tricky part is to change to the 'paused' state. To achieve this, we can set up a javafx.animation.PauseTransition and restart it everytime the text or focus has changed. Eventually, when the transition has finished (e.g. after 3 seconds), it will automatically change the state to 'paused':

Lastly, we change to 'inactive' when focus is lost:

And here's my take on a simple implementation. Enjoy!

import javafx.animation.PauseTransition;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.TextArea;
import javafx.util.Duration;
import rocks.xmpp.extensions.chatstates.model.ChatState;

public class ChatStateTextArea extends TextArea {

    private final PauseTransition pauseTransition = new PauseTransition(Duration.seconds(3));

    private final ReadOnlyObjectWrapper<ChatState> chatState = new ReadOnlyObjectWrapper<>();

    public ChatStateTextArea() {
        // This is the initial state.
        chatState.set(ChatState.INACTIVE);

        focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean
                    aBoolean2) {
                if (aBoolean2) {
                    if (getText().isEmpty()) {
                        // If we have received focus in an empty text field, immediately transition to "active".
                        chatState.set(ChatState.ACTIVE);
                        pauseTransition.stop();
                    } else {
                        // If we have received focus in an non-empty text field, transition to "paused".
                        chatState.set(ChatState.PAUSED);
                        // Start the timer, which will automatically transition to the next state.
                        pauseTransition.playFromStart();
                    }
                } else {
                    pauseTransition.playFromStart();
                }
            }
        });

        textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
                // We are in "composing" state.
                chatState.set(ChatState.COMPOSING);
                // Restart the timer.
                pauseTransition.playFromStart();
            }
        });

        pauseTransition.setOnFinished(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                // When the time is up, switch to "paused", if there's any text, otherwise to active.
                if (isFocused()) {
                    if (getText() != null && !getText().isEmpty()) {
                        chatState.set(ChatState.PAUSED);
                    } else {
                        chatState.set(ChatState.ACTIVE);
                    }
                } else {
                    chatState.set(ChatState.INACTIVE);
                }
            }
        });
    }

    // Ommitted getters and setters for clearness.
}

Saturday, November 1, 2014

Version 0.4.0 now available on Maven Central

I am happy to announce version 0.4.0 of the XMPP client library "Babbler".

First of all thanks to all of you who contributed to this release (reported bugs, tested stuff, made suggestions for improvements, etc.).

There were three main requests you asked for:

  • More configuration options, especially for the connections (e.g. to set a custom SocketFactory, to set custom keep-alive intervals, etc.)
  • Modularization
  • Maven Central integration

The good news is, that this release accounts for everything of it! The bad news is that it's incomptible with the previous version, but hey, that's why it's still in development status.

More Configuration Options

The old connections only had very limited configuration options, which were passed to the constructor. Of course this didn't scale very well when the demand for more configuration options increases. Therefore there's now a configuration object, which let's you configure the connection.

Instead of passing a connection to the XmppSession, you now pass the configuration object to it. This allows you to reuse a configuration for multiple sessions. The session then creates a new connection based on the configuration.

Here's how it looks like.

There's also a configuration object for the whole XmppSession, e.g. to set a debugger. It also takes responsibility to create the JAXBContext, which was previously done in the XmppSession. This has the advantage that it doesn't need to be recreated for each new session (because it is quite expensive to create it).

Modularization

The previous version did a bad job on modularization, in fact you weren't able to only use XMPP core functionality without having to include all extensions. This version now improves on this situation. There's a better separation between core and extensions, so that you only need to include a small jar file, if you only want XMPP core functionality.

Maven Central Integration

Some people asked for it and it has always been a long-term goal: Maven Central integration. Well, it now happened earlier than I thought, this is the first version to be available on Maven Central!

They made me register a domain, so that they could assign a unique groupId: xmpp.rocks.

The new domain also made me feel like changing the package name, in order to fit the groupId and generally it's the convention to use the domain you control for the package name. Given that the API wouldn't be compatible anyway (due to the mentioned new configuration objects and also due to the modularization) I changed the package name to "rocks.xmpp" and also took the opportunity to refactor some other package names. So, if you are migrating from a previous version, you have to refresh your import statements.

Here are the coordinates:


<dependency>
    <groupId>rocks.xmpp</groupId>
    <artifactId>xmpp-core-client</artifactId>
    <version>0.4.0</version>
</dependency>
<dependency>
    <groupId>rocks.xmpp</groupId>
    <artifactId>xmpp-extensions-client</artifactId>
    <version>0.4.0</version>
</dependency>

Visual Debugger

One cool new feature is the visual XMPP viever/debugger (written in JavaFX). I've already blogged about it in my last post, but here's a small glimpse, how it looks like:

There have also been other new features, improvements and bug fixes, which are listed in the changelog.

As always: I am happy about feedback!

Sunday, October 5, 2014

Visual XMPP View Coming in Next Release

Here's some preview what is planned for the next version.

I've added a visual XMPP view / debugger, which helps you debugging and understanding the XMPP traffic.

There are three tabs. The first one shows the connection and presence status.

The second tab shows the XMPP elements being exhanged between the client and the server. This is probably the most interesting tab because you exactly see what's going on.

There are also some neat gimmicks like filtering, searching and highlighting an IQ respone if you have selected an IQ request (as you can see below).

Searching and filtering:

The third tab is less interesting, it basically shows the raw traffic in two text areas one for the incoming and one for outgoing XMPP stream.

I am not sold on the API yet, but what you need to do is probably something like this:

XmppSessionConfiguration configuration = new XmppSessionConfiguration();
// Enable debugging, so that we see something.
configuration.setDebugMode(true);
// Set the graphical debugger
configuration.setDebugger(new VisualDebugger());

// Create the session
XmppSession xmppSession = new XmppSession(null, configuration, new TcpConnection("localhost", 5222));

Sunday, September 14, 2014

Repository Moved from Mercurial to Git

Hi all,

Babbler now uses Git instead of Mercurial. Admittedly I have no extensive experience with either source control system, but judging from my limited experience with both systems, I feel Git is more suited.

In my opinion working with Git's branching model feels more natural, the tools for modifying history are better integrated (no need to install extensions) and the documentation is written more comprehensively.

So, let's give it a try!

The old Mercurial repository is still available here, but will probably be removed in the future.

Sunday, August 31, 2014

Sunday, July 6, 2014

Release 0.2.0 - Create an XMPP Core Library, MUC support and more...

Welcome to another blog post! It's been a long time since my last post and since the last version there have been two major refactorings which are now finished. There are also new features like Multi-User Chat support which have reached a status where they can be considered complete. Reason enough to release version 0.2.0! Let's have a look at the important changes.

Refactoring 1: Create an XMPP Core Library

The first refactoring I am talking about is that Babbler has emerged into two modules: An "xmpp-core" module which mainly consists of mappings between Java classes and XMPP and an "xmpp-client" module for XMPP client functionality, which implements "real" business logic for clients.

The reason for this separation is simple: I wanted to have an "xmpp-core" module, which could serve client and server implementations alike, at least theoretically.

Its purpose is comparable to Tinder, but it's much cleaner, more complete and doesn't have the "jivesoftware stamp" attached to it. Besides mapping to XMPP stanzas and extensions it also provides a few utility methods and classes, e.g. for generating the verification string for Entity Capabilities.

If interest arose, it could now be easily provided as own library, similarly to Tinder.

What you can do with it now is that you can generate XMPP-style XML in an easy way using standard JAXB:

Writer writer = new StringWriter();

XMLStreamWriter xmlStreamWriter = XMLOutputFactory.newFactory().createXMLStreamWriter(writer);
XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter(xmlStreamWriter, true);

JAXBContext jaxbContext = JAXBContext.newInstance(Message.class, Sent.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

Message forwardedMessage = new Message(Jid.valueOf("romeo@example.net"), Message.Type.CHAT, "Hi!!");

Message message = new Message(Jid.valueOf("juliet@example.net"));
message.getExtensions().add(new Sent(new Forwarded(forwardedMessage)));

marshaller.marshal(message, xmppStreamWriter);

System.out.println(writer.toString());

Which would output the following:

<message to="juliet@example.net">
    <sent xmlns="urn:xmpp:carbons:2">
        <forwarded xmlns="urn:xmpp:forward:0">
            <message xmlns="jabber:client" to="romeo@example.net" type="chat">
                <body>Hi!!</body>
            </message>
        </forwarded>
    </sent>
</message>

Cool, isn't it? Especially, that it sets the correct namespace for the inner message, but doesn't set it for the outer message (since it's the default namespace there).

Refactoring 2: XmppSession Instead of Connection

The second major refactoring was the concept of using an "XmppSession" instead of a "Connection". The session can have multiple connection methods which are tried during establishing the XMPP session. If connecting with TCP fails for some reason, the session can try alternative connection methods such as BOSH as fallback.

I've illustrated this approach here:

New Features

Besides these major refactorings, there were other changes in form of new features, improvements and more documentation.

One of the major addition is the Multi-User Chat support. In my last blog post, I've also announced support for Real-Time Text would find its way into 0.2.0, but unfortunately it's not yet finished.

You can find the changelog and documentation on the project site.

---

That's it for now, I hope you like it. If you have something to say, just leave a comment!