<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tobias Nix</title>
    <description>The latest articles on DEV Community by Tobias Nix (@tobiasnix).</description>
    <link>https://dev.to/tobiasnix</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3395341%2F643fc1cf-0716-4d4d-aae6-65f0167e9a5a.jpeg</url>
      <title>DEV Community: Tobias Nix</title>
      <link>https://dev.to/tobiasnix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tobiasnix"/>
    <language>en</language>
    <item>
      <title>TAK-Server Plugin - Smack.xml and plugin loader errors</title>
      <dc:creator>Tobias Nix</dc:creator>
      <pubDate>Mon, 28 Jul 2025 21:58:12 +0000</pubDate>
      <link>https://dev.to/tobiasnix/tak-server-plugin-smackxml-and-plugin-loader-errors-3m3g</link>
      <guid>https://dev.to/tobiasnix/tak-server-plugin-smackxml-and-plugin-loader-errors-3m3g</guid>
      <description>&lt;p&gt;Hey devs 👋,&lt;/p&gt;

&lt;p&gt;I'm working on integrating a custom plugin into the TAK Server (takserver-sender-receiver-xmpp-server-plugin-1.0.12.jar) that uses the Smack XMPP library (v4.4.6). The plugin should forward messages to an Openfire XMPP server, but I'm running into two core issues:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. takserver-plugins.sh Classpath Issue?
&lt;/h4&gt;

&lt;p&gt;I'm unsure whether the takserver-plugins.sh script is correctly setting the classpath. Here's the file:&lt;br&gt;
takserver-plugins.sh&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh
. ./setenv.sh

# Hier nur System-Properties, kein "-jar"!
export JDK_JAVA_OPTIONS="-Dloader.path=WEB-INF/lib-provided,WEB-INF/lib,WEB-INF/classes,/opt/tak/lib,/opt/tak/lib/deps -Dio.netty.tmpdir=/opt/tak -Djava.io.tmpdir=/opt/tak -Dio.netty.native.workdir=/opt/tak -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:/dev/./urandom -DIGNITE_UPDATE_NOTIFIER=false -DIGNITE_QUIET=true -Djdk.tls.client.protocols=TLSv1.2 -Dsmack.xmlparser=org.jivesoftware.smack.xml.xpp3.Xpp3XmlPullParserFactory"

exec java -server -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -Xmx${PLUGIN_MANAGER_MAX_HEAP}m -jar takserver-pm.jar "$@"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All .jar files are in /opt/tak/lib or /opt/tak/lib/deps, and the plugin starts loading—but maybe loader.path or the way -jar takserver-pm.jar is invoked interferes?&lt;/p&gt;

&lt;p&gt;Is there a better way to ensure the Smack library dependencies and plugin JAR are correctly picked up?&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Smack Initialization Error: XML Parser Not Found
&lt;/h4&gt;

&lt;p&gt;The TAK Server logs show this when the plugin attempts to initialize, before I edited the takserver-plugins.sh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-07-28-11:20:24.823 [ForkJoinPool-3-worker-1] tak.server.plugins.XmppPlugin - Starting XMPP Plugin...
2025-07-28-11:20:24.823 [ForkJoinPool-3-worker-1] tak.server.plugins.XmppPlugin - Initialisiere XMPP mit joy@189.65.91.182:5222 (EmpfÃ¤nger: james@openfire.recce8.heer)
2025-07-28-11:20:24.867 [main] t.s.p.messaging.PluginMessenger - starting PluginMessenger
2025-07-28-11:20:24.868 [main] t.s.plugins.service.PluginService - Started PluginService in 58.843 seconds (process running for 77.705)
Exception in thread "ForkJoinPool-3-worker-1" java.lang.ExceptionInInitializerError
    at org.jivesoftware.smack.Smack.getVersion(Smack.java:38)
    at org.jivesoftware.smack.Smack.ensureInitialized(Smack.java:64)
    at org.jivesoftware.smack.ConnectionConfiguration.&amp;lt;clinit&amp;gt;(ConnectionConfiguration.java:116)
    at tak.server.plugins.XmppPlugin.start(XmppPlugin.java:55)
    at tak.server.plugins.PluginBase.internalStart(PluginBase.java:39)
    at tak.server.plugins.PluginStarter.lambda$startReceiverPlugins$9(PluginStarter.java:221)
    at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1395)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Caused by: java.lang.IllegalStateException: Could not parse Smack configuration file
    at org.jivesoftware.smack.SmackInitialization.&amp;lt;clinit&amp;gt;(SmackInitialization.java:106)
    ... 12 more
Caused by: java.lang.IllegalStateException: No XmlPullParserFactory registered with Service Provider Interface (SPI). Is smack-xmlparser-xpp3 or smack-xmlparser-stax in classpath?
    at org.jivesoftware.smack.xml.SmackXmlParser.getXmlPullParserFactory(SmackXmlParser.java:41)
    at org.jivesoftware.smack.xml.SmackXmlParser.newXmlParser(SmackXmlParser.java:65)
    at org.jivesoftware.smack.util.PacketParserUtils.getParserFor(PacketParserUtils.java:80)
    at org.jivesoftware.smack.SmackInitialization.processConfigFile(SmackInitialization.java:159)
    at org.jivesoftware.smack.SmackInitialization.processConfigFile(SmackInitialization.java:154)
    at org.jivesoftware.smack.SmackInitialization.&amp;lt;clinit&amp;gt;(SmackInitialization.java:103)
    ... 12 more
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But these files are present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tak-admin@tak-server:/opt/tak/lib$ tree
.
├── deps
│&amp;nbsp;&amp;nbsp; ├── jxmpp-core-1.1.0.jar
│&amp;nbsp;&amp;nbsp; ├── jxmpp-jid-1.1.0.jar
│&amp;nbsp;&amp;nbsp; ├── jxmpp-util-cache-1.1.0.jar
│&amp;nbsp;&amp;nbsp; ├── minidns-client-1.0.5.jar
│&amp;nbsp;&amp;nbsp; ├── minidns-core-1.0.5.jar
│&amp;nbsp;&amp;nbsp; ├── smack-core-4.4.6.jar
│&amp;nbsp;&amp;nbsp; ├── smack-debug-4.4.6.jar
│&amp;nbsp;&amp;nbsp; ├── smack-extensions-4.4.6.jar
│&amp;nbsp;&amp;nbsp; ├── smack-im-4.4.6.jar
│&amp;nbsp;&amp;nbsp; ├── smack-tcp-4.4.6.jar
│&amp;nbsp;&amp;nbsp; ├── smack-xmlparser-4.4.6.jar
│&amp;nbsp;&amp;nbsp; ├── smack-xmlparser-stax-4.4.6.jar
│&amp;nbsp;&amp;nbsp; ├── smack-xmlparser-xpp3-4.4.6.jar
│&amp;nbsp;&amp;nbsp; └── xpp3-1.1.4c.jar
└── takserver-sender-receiver-xmpp-server-plugin-1.0.12.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still, Smack doesn't detect the XML parser. Any idea how to properly register the parser or make Smack pick it up?&lt;/p&gt;

&lt;p&gt;For the sake of completeness, here my plugin code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package tak.server.plugins;

import java.lang.invoke.MethodHandles;

import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import atakmap.commoncommo.protobuf.v1.MessageOuterClass.Message;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * TAK Plugin: Leitet empfangene TAK Nachrichten per XMPP an einen OpenFire-User weiter.
 */
@TakServerPlugin(name = "XMPP Forwarder Plugin", description = "Leitet TAK Nachrichten an XMPP weiter")
public class XmppPlugin extends MessageReceiverBase {

    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    private AbstractXMPPConnection xmppConnection;
    private EntityBareJid xmppRecipient;

    private final ArrayBlockingQueue&amp;lt;Message&amp;gt; messageQueue = new ArrayBlockingQueue&amp;lt;&amp;gt;(1000);
    private static final ScheduledExecutorService worker = Executors.newScheduledThreadPool(1);
    private ScheduledFuture&amp;lt;?&amp;gt; future;

    @Override
public void start() {
    logger.info("Starting XMPP Plugin...");

    try {
        String username = (String) config.getProperty("xmppUsername");
        String password = (String) config.getProperty("xmppPassword");
        String domain = (String) config.getProperty("xmppDomain");
        String host = (String) config.getProperty("xmppHost");
        int port = (int) config.getProperty("xmppPort");
        String recipient = (String) config.getProperty("xmppRecipient");

        logger.info("Initialisiere XMPP mit {}@{}:{} (Empfänger: {})", username, host, port, recipient);

        xmppRecipient = JidCreate.entityBareFrom(recipient);

        XMPPTCPConnectionConfiguration connectionConfig = XMPPTCPConnectionConfiguration.builder()
            .setXmppDomain(domain)
            .setHost(host)
            .setPort(port)
            .setUsernameAndPassword(username, password)
            .setSecurityMode(XMPPTCPConnectionConfiguration.SecurityMode.ifpossible)  // try TLS, but fallback
            .setResource("tak-plugin")
            .setCompressionEnabled(false)
            .setSendPresence(false)
            .build();

        xmppConnection = new XMPPTCPConnection(connectionConfig);

        logger.info("Starte Verbindungsaufbau...");
        xmppConnection.connect();
        logger.info("Verbindung aufgebaut – jetzt login...");
        xmppConnection.login();
        logger.info("XMPP-Verbindung zu {} erfolgreich", domain);

        future = worker.scheduleWithFixedDelay(() -&amp;gt; {
            try {
                Message msg = messageQueue.take();
                forwardToXmpp(msg);
            } catch (InterruptedException e) {
                logger.error("Nachrichtenverarbeitung unterbrochen", e);
            }
        }, 0, 10, TimeUnit.MILLISECONDS);

    } catch (Exception e) {
        logger.error("Fehler beim Initialisieren der XMPP-Verbindung: {}", e.getMessage(), e);
    }
}

    @Override
    public void onMessage(Message message) {
        if (xmppConnection != null &amp;amp;&amp;amp; xmppConnection.isConnected()) {
            messageQueue.offer(message);
            logger.info("TAK-Nachricht in Warteschlange gestellt");
        } else {
            logger.warn("XMPP-Verbindung nicht aktiv – Nachricht verworfen");
        }
    }

    private void forwardToXmpp(Message message) {
        try {
            String callsign = message.getPayload().getCotEvent().getDetail().getContact().getCallsign();
            String content = message.getPayload().getCotEvent().getDetail().getXmlDetail();

            String body = String.format("Von: %s\n%s", callsign, content);

            ChatManager chatManager = ChatManager.getInstanceFor(xmppConnection);
            Chat chat = chatManager.chatWith(xmppRecipient);
            chat.send(body);

            logger.info("Nachricht an {} gesendet:\n{}", xmppRecipient.asBareJid(), body);
        } catch (Exception e) {
            logger.error("Fehler beim Senden der Nachricht über XMPP", e);
        }
    }

    @Override
    public void stop() {
        if (future != null) {
            future.cancel(true);
        }
        if (xmppConnection != null &amp;amp;&amp;amp; xmppConnection.isConnected()) {
            xmppConnection.disconnect();
            logger.info("XMPP-Verbindung wurde getrennt");
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope these are all infos to help me out, if you need more I will them provide as fast as I can.&lt;/p&gt;

&lt;p&gt;Thanks for your help, you're the best!&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
