Smack XMPP Client Library

repository·master·Indexed 25 days ago

https://github.com/igniterealtime/smack

An open-source, highly modular XMPP client library written in Java for Java SE compatible JVMs and Android applications. Smack enables the development of instant messaging clients and M2M/IoT integrations, providing tools for parsing XMPP XML streams via providers, implementing XmppInputOutputFilter for stream interception, and managing Ad-Hoc commands for server-side service execution.

Tokens
7.4K
Snippets
10
Records
50
Agent score
81%

What's inside Smack

  1. Requirements for Stanza subclasses

    master

    All classes that subclass TopLevelStreamElement or ExtensionElement must satisfy the following requirements:

    1. Immutability/Cloning: They must either be immutable (ideally providing a Builder) OR implement TypedCloneable. (Immutability is the preferred option).
    2. Serialization: They must be Serializable.

    These rules ensure that cloning is handled correctly and that immutable instances do not need to be cloned unnecessarily.

  2. How Smack Providers work

    master

    Providers are responsible for parsing the XMPP XML stream into new Java objects.

    When implementing a provider, follow these patterns:

    • Use a while(true) loop with a labeled break (e.g., outerloop:) to handle the parsing lifecycle.
    • Use switch/case on XmlPullParser.Event (integers) for the main loop and switch/case on element names (Strings) for sub-elements.
    • Use XmlPullParser.nextText() only when an element is required to have text.
    • Use ParserUtils to parse Java primitives (e.g., ParserUtils.getBooleanAttribute, ParserUtils.getIntegerAttribute).
    • Design the resulting classes to be as immutable as possible.

    Common Pitfall: Use a long instead of int when the XML schema specifies xs:unsignedInt, as Java's int range is too small for this type.

    public MyExtension parse(XmlPullParser parser, int initialDepth) {
      MyElement myElement = null;
      MyInfo myInfo = null;
      String attrFoo = parser.getAttributeValue("", "attrFoo");
    
      outerloop: while(true) {
        XmlPullParser.Event event = parser.next();
    
        switch (event) {
        case START_ELEMENT:
          String name = parser.getName();
          switch(name) {
          case "myElement":
            myElement = new MyElement(parser.nextText());
            break;
          case "myInfo":
            boolean alpha = ParserUtils.getBooleanAttribute(parser, "alpha");
            int delta = ParserUtils.getIntegerAttribute(parser, "delta");
            myInfo = new MyInfo(alpha, delta);
            break;
          }
          break;
        case END_ELEMENT:
          if (parser.getDepth() == initialDepth) {
            break outerloop;
          }
          break;
        default:
          break;
        }
      }
    
      return new MyExtension(attrFoo, myElement, myInfo);
    }
  3. Build Smack on Windows

    master

    Smack requires a case-sensitive file system. On Windows 10 (v1803 or higher), you can enable case sensitivity for specific folders using fsutil.exe in an Administrator console.

    fsutil.exe file SetCaseSensitiveInfo C:\git\Smack enable
    cd \git\Smack
    git clone git@github.com:igniterealtime/Smack.git
    cd Smack
    gradle assemble
  4. Report bugs and get support

    master

    If you encounter issues, follow these steps to ensure your report is handled correctly:

    1. Search first: Check the Bug Tracker to see if your issue has already been reported.
    2. Read the guide: Review the "How to ask for help or report an issue" wiki page.
    3. Use Discourse: Create an account on the Discourse forum and post your question under the 'Smack Support' sub-category.

    For real-time discussion, developers are available on XMPP at smack@conference.igniterealtime.org.

  5. Build Smack on macOS

    master

    Smack requires a case-sensitive file system. Since macOS is case-insensitive by default, you must create a case-sensitive APFS volume using Disk Utility before building.

    1. Launch Disk Utility (Applications > Utilities).
    2. Click the + button or go to Edit > Add APFS Volume.
    3. Name the volume (e.g., Smack).
    4. Change the format to APFS (Case-sensitive).
    5. Click Add.

    Once the volume is mounted (e.g., at /Volumes/Smack), proceed with the build:

    cd /Volumes/Smack
    git clone git@github.com:igniterealtime/Smack.git
    cd Smack
    gradle assemble
  6. Configure Eclipse and IntelliJ IDEA IDE settings

    master

    To ensure code formatting and import ordering pass CheckStyle rules, import the provided configuration files:

    • Eclipse: Import settings from ./resources/eclipse/.
    • IntelliJ IDEA: Import Java Code Style settings from ./resources/intellij/smack_formatter.xml.

    Note: IntelliJ IDEA may require a restart after applying new rules for them to take effect.

  7. How Ad-Hoc commands work in Smack

    master

    An Ad-Hoc command is an abstraction for executing a service on an XMPP server and managing the resulting execution state. Each command instance is responsible for storing the results of its execution (such as data forms) and managing its lifecycle.

    Key Concepts:

    • Nodes: Each command has a node that must be unique within a given JID.
    • Stages: Commands can consist of multiple stages. These stages are used to gather information required for execution. Users can move through stages using allowed actions like prev or next.
    • Actions: During a stage, a user can perform specific actions. While a command cannot be cancelled while actively executing, a user can request a cancel action when submitting a stage response to abort the execution and release collected information.
    • Session ID: Each execution is associated with a sessionId to track the state across multiple requests/stages.

    Lifecycle and State:

    • Completion: A command is considered finished when its status reaches AdHocCommandData.Status.completed (checked via isCompleted()).
    • Error Handling: Commands may throw XMPPException during actions. Specific error conditions (like malformed-action or session-expired) can be extracted from the StanzaError to understand the failure reason.
  8. How AdHocCommandManager manages command lifecycles

    master

    The AdHocCommandManager handles both the server-side (hosting commands) and client-side (executing remote commands) aspects of the AdHoc Commands protocol.

    Server-side (Hosting)

    When you register commands, the manager:

    1. Exposes via Service Discovery: Automatically adds the command nodes to the connection's service discovery information.
    2. Handles Requests: Listens for incoming IQ requests. If a request has no sessionId, it starts a new session. If it has a sessionId, it resumes an existing execution.
    3. Manages Multi-stage Sessions: Supports commands that require multiple steps (e.g., next, prev, complete, cancel). It tracks the state of these sessions in an executingCommands map.
    4. Session Cleanup: A background 'sweeper' thread periodically removes expired sessions based on the configured sessionTimeoutSecs to prevent memory leaks.

    Client-side (Executing)

    When you use getRemoteCommand, you receive a proxy object. This allows you to interact with remote commands using a local API, abstracting the underlying XMPP stanzas.

  9. Understand the structure of AdHocCommandData

    master

    The AdHocCommandData object represents the data returned by an Ad-Hoc command execution. It is constructed via an AdHocCommandDataBuilder and contains information about the command's status, the action being performed, session identifiers, associated data forms, notes, and potential errors.

    Key components of an AdHocCommandData object include:

    • Session ID: A unique identifier for the command session.
    • Status: The current state of the command, which can be executing, completed, or canceled.
    • Action: The specific action being taken (e.g., via the action attribute).
    • Allowed Actions: A list of possible next steps, such as next, complete, or prev.
    • Data Form: An optional DataForm (from the jabber:x:data namespace) containing structured data.
    • Notes: A collection of AdHocCommandNote objects, which can be categorized by type (e.g., info).
    • Error: A StanzaError if the command failed.
  10. Use SingleStage for simple AdHoc commands

    master

    If your Ad-Hoc command only requires a single step of execution, extend AdHocCommandHandler.SingleStage instead of the base class. This simplifies implementation by automatically handling the command lifecycle for a one-off action.

    You only need to implement one method:

    • executeSingleStage(AdHocCommandDataBuilder response)

    The SingleStage implementation automatically sets the status to completed and prevents invalid transitions to next, complete, or prev by throwing a bad_request XMPP error.