External Components are like server plugins, which are not hosted within the server environment,
but instead connect externally to the server via the “Jabber Component Protocol”. Usually, components “live” in their
own subdomain, e.g. mycomponent.xmpp.rocks
. Like internal plugins they allow you to enhance a server's functionality,
e.g. provide additional services.
Such external components connect to the server and authenticate via a shared secret. Component and server then communicate with each other using XMPP.
Clients could send messages (or other stanzas) to the component (e.g. to mycomponent.xmpp.rocks
) and the component
could do whatever it wants, like doing statistics, sending or forwarding messages, returning IQ results, or providing
translation services.
Here's a sample of a component:
ExternalComponent myComponent = ExternalComponent.create("translation", "sharedSecret", "localhost", 5275);
It connects to a server hosted at localhost
which allows external components to connect on port 5275.
The component name is translation
, i.e. it will usually also be addressable via the correspondent subdomain,
i.e. translation.xmpp.rocks
.
The shared secret (password), is configured in the server and is sharedSecret
here.
Before connecting you should setup any listeners or other configuration, e.g.:
ServiceDiscoveryManager serviceDiscoveryManager = myComponent.getManager(ServiceDiscoveryManager.class);
// Add an identity for the component. This will be used by clients who want to discover the translation service.
serviceDiscoveryManager.addIdentity(Identity.automationTranslation().withName("Translation Provider Service"));
// Our component supports the XEP-0171 protocol, let's advertise it by including the protocol name in the feature list,
// so that clients can discover our component as language translation service and can send queries to it.
myComponent.enableFeature(LanguageTranslation.NAMESPACE);
// Don't advertise the MUC feature. We are no chat service.
myComponent.disableFeature(Muc.NAMESPACE);
// Don't advertise the SOCKS bytestreams feature. We are no stream proxy.
myComponent.disableFeature(Socks5ByteStream.NAMESPACE);
// Listen for language support queries.
myComponent.addIQHandler(LanguageSupport.class, new AbstractIQHandler(IQ.Type.GET) {
@Override
protected IQ processRequest(IQ iq) {
// The client/requester likes to discover language support of our component.
// Return a list of supported languages.
return iq.createResult(new LanguageSupport(Collections.singleton(new LanguageSupport.Item("en", myComponent.getDomain(), "de", "testEngine", true, null))));
}
});
// Listen for translation queries
myComponent.addIQHandler(LanguageTranslation.class, new AbstractIQHandler(IQ.Type.GET) {
@Override
protected IQ processRequest(IQ iq) {
// The client/requester likes to translate something.
Collection<LanguageTranslation.Translation> translations = new ArrayDeque<>();
LanguageTranslation translation = iq.getExtension(LanguageTranslation.class);
// Do a real translation here and return proper results in accordance with XEP-0171.
translations.addAll(translation.getTranslations().stream()
.map(t -> LanguageTranslation.Translation.forDestinationLanguage(t.getDestinationLanguage())
.withSourceLanguage(translation.getSourceLanguage())
.withTranslatedText("HALLO"))
.collect(Collectors.toList()));
LanguageTranslation languageTranslation = new LanguageTranslation(translations);
return iq.createResult(languageTranslation);
}
});
myComponent.connect();
Now, when a client discovers services of your server, the server will advertise your component as “Translation Provider Service” and clients could ask your component to translate a text.
ExternalComponent
is in many things similar to XmppClient
because they derive from the same base class XmppSession
.
That also means, that there's a lot of shared functionality and ExternalComponent
supports many protocols out of the
box, like Service Discovery, Message Delivery Receipts, Entity Time, …