Openfire Logo

Custom Group Provider Guide

Introduction

This is a guide for developers that wish to integrate Openfire with their own group system.

This integration requires some Java knowledge in order to implement a custom group provider for Openfire. The skill needed will vary depending on what you are trying to achieve.

Topics that are covered in this document:

Background

Under standard configuration, Openfire maintains group data in its own database tables. Various alternatives to this are offered that allow you to use groups defined in Active Directory or LDAP or obtaining groups from custom database tables.

If you're interested in integrating with a system that is not compatible with the standard integration options that are provided by Openfire, then you can implement a custom integration. This guide will help you get started!

The GroupProvider extension point

Openfire's API defines the GroupProvider interface, which is the extension point to use when implementing custom group functionality. It is noteworthy that many implementations choose to inherit from AbstractGroupProvider. This abstract class provides common functionality typically used by implementations. You are, of course, free to write your own implementation that does not extend on AbstractGroupProvider!

The default implementation of this provider is the DefaultGroupProvider, which as the name suggests is the version of this provider Openfire will use if not overridden. It uses the ofGroup, ofGroupUser and ofGroupProp database tables.

The steps to get Openfire using a custom GroupProvider are described below.

  1. Write a class that implements GroupProvider, providing your own business logic.
  2. Make the class available in a jar and make this available to Openfire by placing it in the lib directory. Why not use an Openfire plugin, you ask? Read the FAQ entry below! There are numerous ways to package a jar with this class inside it, popular build systems such as Gradle and Maven can make your life easier.
  3. Set the property provider.group.className to be the full name of your class, e.g. org.example.CustomGroupProvider. You can easily do this by defining such a property in the conf/openfire.xml configuration file, as shown below.
    Example openfire.xml configuration snippet
    <provider>
        <group>
            <className>org.example.CustomGroupProvider</className>
        </group>
    </provider>
  4. Restart Openfire. Your custom class should now be providing user groups to Openfire.

It is worth noting that the GroupProvider interface defines an interface that can be used to both read data from, but also write data back to the system that is being integrated with. If the system that you're integrating with can not or must not have its user definitions changed, you can implement your provider as being 'read-only'.

To mark your provider as being read-only, implement the isReadOnly() method to return the value true. All methods that would cause a change of data won't be invoked by Openfire if you do so. These methods should receive an implementation that throws java.lang.UnsupportedOperationException.

A similar approach (throwing java.lang.UnsupportedOperationException) can be used to prevent implementing optional functionality, such as the support group properties.

Frequently Asked Questions

Do I have to compile my custom class into the Openfire jar?

No, the class only needs to be visible on the Openfire classpath.

How do I ensure my custom class is visible on the Openfire classpath?

Just place your new custom library in the Openfire lib directory, this will ensure it is automatically available at startup.

Should I include all the dependencies that my code needs?

Yes, and no. Your code will run using the same classpath as Openfire. Openfire's dependencies are already on that classpath. If your code uses the same dependencies, you do not need to include them again. You can add other dependencies, either by packaging them in your jar file, or by placing them in the openfire lib folder.

Beware of conflicting dependencies! Sometimes, packaging dependencies in an all-containing jar, and renaming the packages of those dependencies (shade them), can be helpful.

Isn't it easier to deploy my code as an Openfire plugin?

It is not recommended to use a plugin for packaging and deploying your provider. For one, plugins use a dedicated class path. For another, the load order of plugins could lead to hairy scenarios, and finally: plugins can be unloaded at runtime. This all could lead to a running Openfire service for which you cannot guarantee that your provider is loaded at all times. For these reasons, we recommend creating a jar file instead of an Openfire plugin, and placing that jar file in Openfire's lib directory!

Can I see some examples?

Openfire's own group functionality makes use of the GroupProvider API! If you want to get some inspiration, you can have a look at the implementations of this interface that are part of Openfire, such as the ones below.

Note that these providers are but a sample of the available providers. Discover more providers by using your IDE to find implementations of the interface!

Will I have a degradation in performance using a custom GroupProvider?

It completely depends on your implementation. As with any Openfire customisation or plugin, badly written code has the potential to cause Openfire to perform slower. Use performance testing tools such as Tsung to ensure issues haven't been introduced.

How can I have my custom class connect to another DB/Web service/NoSQL store etc?

This is out of the scope of this documentation and is your choice as a developer. If you are looking to externalize properties like connection details, the Openfire properties mechanism and the JiveGlobals class are good places to start investigating.