Developing Jahia extensions
Extension points
Multiple parts of Jahia can be extended by adding Java classes in modules. A module developer can, for example, add a new action that will add a new HTTP endpoint, add a render filter that will change the rendering, or modify the HTML cache key generation by adding CacheKeyPartGenerator.
In versions before Jahia 8, this was usually done by declaring Spring Beans. These beans were processed and registered into the Jahia core. Now you can do the same thing with OSGi services, without relying on any Spring context. You just need to expose the same classes as OSGi services with the same properties.
Using Declarative Services to build extensions
All extension points in Jahia should be implemented using OSGi Declarative Services. This section shows how to achieve this before looking at the various extension types.
Code samples
Sample code showing how to extend Jahia with Declarative Services (DS) are available at https://github.com/Jahia/OSGi-modules-samples.
Simple component
When using Declarative Services, you need to add the @Component
annotation to declare your class as DS component with the service attribute set to the base class. The component can set its attribute directly in its activator:
@Component(service = RenderFilter.class, immediate = true)
public class TestFilter extends AbstractFilter {
@Activate
public void activate() {
setPriority(1);
setApplyOnConfigurations("page");
}
Configured component
A component can also rely on an OSGi configuration. By default, the configuration has the same name as the component, which is the fully qualified class name. In the following example, the content of <data>/karaf/etc/org.mypackage.MyChoiceListInitializer.cfg
is passed in the props parameter of the activator.
@Component(service = {ModuleChoiceListInitializer.class, ModuleChoiceListRenderer.class})
public class MyChoiceListInitializer extends AbstractChoiceListRenderer implements ModuleChoiceListRenderer, ModuleChoiceListInitializer {
@Activate
public void activate(Map<String, ?> props) {
setKey((String) props.get("key"));
}
}
You can also use an annotation type containing the properties you expect instead of the generic Map<String, ?>
type. This allows you to have typed attributes and default values.
@interface Param {
String key() default "default-key";
}
@Activate
public void activate(Param p) {
setName(p.key());
}
Note that you can provide configuration files in modules in resources/META-INF/configurations
. They will be copied to <data>/karaf/etc
.
Component factories
Another useful feature of DS components is the component factory. You can create multiple instances of your beans based on multiple configuration files. Compared to the previous example, you need to modify the component scope to Bundle
, as in the following example:
@Component(service = RenderFilter.class, scope = ServiceScope.BUNDLE)
public class MyFilter extends AbstractFilter {
@Activate
public void activate(Map<String, ?> props) {
try {
BeanUtils.copyProperties(this, props);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
You can then define multiple configuration files, all named org.mypackage.MyFilter-<id>.cfg
, with a different id for each config. One instance of the component is created for each configuration.
Hot reload
Another benefit of DS components is that it allows hot reloading of components without requiring server restarts. For example, you can use Declarative Services to add a servlet filter (by extending AbstractServletFilter
) and deploy without requiring a server restart to activate. (See sample code https://github.com/Jahia/OSGi-modules-samples/tree/master/servlet-filter-samples).
Extension types
Basic usage
Custom GraphQL Query implementation
Definition | Adding new queries to Jahia’s GraphQL endpoint |
Usage | Allows GraphQL clients to retrieve data using custom logic, for example to integrate with a single-page application built using a front-end framework, such as ReactJS or Angular |
Example | Retrieve data from a third-party server or perform some query/aggregation on JCR content |
Class/Interface |
|
More details | See GraphQL extensions |
Sample code | https://github.com/Jahia/graphql-core/tree/master/graphql-extension-example |
Custom GraphQL Mutation implementation
Definition | Adding new mutations to Jahia’s GraphQL endpoint |
Usage | Allows GraphQL clients to send data to custom logic hosted in a Jahia module, for example to have a single-page application perform some state-changing event or forward to a third-party server application. |
Example | Send data collected from the user to a custom backend service |
Class/Interface | org.jahia.modules.graphql.provider.dxm.DXGraphQLExtensionsProvider |
More details | See GraphQL extensions |
Sample code | https://github.com/Jahia/graphql-core/tree/master/graphql-extension-example |
Custom Actions
Definition | Defines a new action that can operate on a content object by exposing a new HTTP endpoint |
Usage | Actions integrate with Jahia’s URL scheme to make it easy to call custom code on a content object using a simple HTTP request |
Example | Use an action to process a form submission |
More details | See Manipulating content with APIs>Actions |
Sample code | https://github.com/Jahia/OSGi-modules-samples/blob/master/action-samples/src/main/java/org/foo/modules/actions/SimpleAction.java Note the usage of the @Activate method that initializes requireAuthenticatedUser, requirePermission, and other setters. |
Custom Render filters
Definition | Makes it possible to control the rendering of any content element, such as a page or simple text node |
Usage | Built-in Jahia render filters including HTML caching, but basically any custom code can be called before and after a content node is rendered, making the possibilities endless |
Example | Use a render filter to personalize the output based on the location of the IP address |
Class/Interface | org.jahia.services.render.filter.RenderFilter |
More details | See Rendering content>Filters |
Sample code | https://github.com/Jahia/OSGi-modules-samples/blob/master/filter-samples/src/main/java/org/foo/modules/filters/SimpleFilter.java |
Custom Choicelist Initializer
Definition | Custom code used to populate a dropdown choice list in Content Editor |
Usage | ChoiceList initializers enable the use of custom logic to populate the content of a field in Content Editor. This applies to many different uses cases, ranging from retrieving content from a content list to calling a third party service to retrieve choice list values |
Example | Create a choice list of products by connecting to a PIM and retrieving available products |
Class/Interface | org.jahia.services.content.nodetypes.initializers.ModuleChoiceListInitializer |
More details | See Defining choicelist initializers and Enhancing content types for editors>Choicelists. Also see the Choicelist developer training topic. |
Sample code | https://github.com/Jahia/OSGi-modules-samples/blob/master/choicelist-samples/src/main/java/org/foo/modules/choicelists/SimpleChoiceListInitializer.java |
Authentication
Custom Authentication Valve
Definition | Provides a way to extend Jahia’s authentication mechanism to connect to any custom authentication service |
Usage | Using custom authentication valves, you can extend Jahia’s built-in system to integrate it with third-party auth services. Valves don’t replace other built-in valves but rather are used in order of priority, making it possible to use multiple auth systems at the same time. |
Example | Use a custom valve to provide authentication from social media services such as Facebook or Twitter |
Class/Interface | org.jahia.params.valves.BaseAuthValve |
More details | See About the Jahia authentication layer>Implementing custom authentication valve |
Sample code | https://github.com/Jahia/OSGi-modules-samples/blob/master/auth-valve/src/main/java/org/foo/modules/valve/MyValve.java |