Writing XWiki components

Invalid macro parameters used for the [toc] macro. Cause: [Failed to validate bean: [must be greater than or equal to 1]]. Click on this message for details.

This tutorial guides you through the creation of an XWiki component, which replaces the Plugin architecture and which is now the recommended way of writing XWiki modules. They should be able to execute any Java code and communicate with XWiki by using the existing XWiki (core) components, as well as being exposed to the XWiki documents scripting environment (velocity and groovy).

You should start by reading the Reference document on XWiki Components.

The tutorial below is slightly outdated since some changes have been brought to the Component Module since it was written. See the Reference document on XWiki Components for fresh information. This tutorial needs to be rewritten and duplicate with the Reference document removed.

Let's get started!

Enough talking, let's see some code!

In the followings we will guide you through writing a simple component, helping you to quickly get oriented in XWiki components world and explaining how it works.

Creating a XWiki component using maven

To simplify the three steps process of component creation in XWiki, and since the XWiki code lifecycle is based on maven, we have created a maven archetype to help create a simple component module with a single command, with respect to the XWiki architecture and components specific requirements.

  • download the archetype from here: xwiki-archetype-component-1.0-SNAPSHOT.jar (it will soon be uploaded on our maven repository).
  • use maven to install this file on your local repository by executing (make sure you replace path-to-jar-file with your own path):
mvn install:install-file -Dfile=<path-to-jar-file> -DartifactId=xwiki-archetype-component -DgroupId=com.xpn.xwiki.platform.tools -Dversion=1.0-SNAPSHOT -Dpackaging=jar
  • now you're ready to use maven to generate the xwiki component based on this archetype. Navigate to the directory where you want your component to be located and type:
mvn archetype:generate -DarchetypeGroupId=com.xpn.xwiki.platform.tools -DarchetypeArtifactId=xwiki-archetype-component -DarchetypeVersion=1.0-SNAPSHOT -DgroupId=<component-group-id> -DartifactId=<component-artifact-id>  -Dpackage=<component-package> -Dversion=<component-version> -Dpackaging=jar

where you replace component-group-id, component-artifact-id, component-package, component-version with the corresponding values for your component. To create a server XWiki Watch component, for example, we used -DgroupId=com.xpn.xwiki.products -DartifactId=xwiki-watch-component -Dpackage=org.xwiki.watch.component -Dversion=1.1-SNAPSHOT. Don't forget to follow the xwiki package names guidelines.

Now this will create a new maven module in a folder named component-artifact-id in your folder, with a default xwiki component inside. Note that if your parent (current, from where you are executing maven) folder is the folder of a maven module (contains a pom.xml file), then the command above will fail unless the module is packaged as pom. If the project is packaged as pom, then the newly created module will be added in its modules list, and the parent of the newly created component module will be set to this project's pom.

The component explained

Assume, for the following explanations, that the package you used is org.xwiki.component

Navigating in the component project folder, you will see standard maven project structure like this:

pom.xml
src/main/java/org/xwiki/component/HelloWorld.java
src/main/java/org/xwiki/component/internal/DefaultHelloWorld.java
src/main/resources/META-INF/components.txt
src/test/java/org/xwiki/component/HelloWorldTest.java

which corresponds to the default files created: the HelloWorld interface (service), its implementation DefaultHelloWorld, a test class for this component HelloWorldTest, the component declaration file components.txt and the maven project pom file.

If we have a look in the pom, we see something like this:

<groupId>your-group-id</groupId>
 <artifactId>your-artifact-id</artifactId>
 <version>your-version</version>

which are the group, artifact and version you used when you created your component

<properties>
   <!-- TODO: remove this if you inherit a project that has the core version set -->
   <platform.core.version>1.8-SNAPSHOT</platform.core.version>
 </properties>
It defines the core version for the <dependencies>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-component-api</artifactId>
     <version>${platform.core.version}</version>
   </dependency>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>    
   <!-- Add here all your other dependencies -->
 </dependencies>
The code above defines the dependency on the

To inherit all the properties of the platform projects (among others, source and target compatibility with Java 1.5), we inherit our project from the platform pom:

<parent>
   <groupId>org.xwiki.platform</groupId>
   <artifactId>xwiki</artifactId>
   <version>28</version>
 </parent>
The interface file ( @ComponentRole /* annotation used for declaring the service our component will provide */
public interface HelloWorld
{
   String sayHello()
}
Keep in mind that only this interface specifies the functions the other components will use to communicate with our component, no other functions besides the ones defined in this interface will be accessible to the "outside world". In our case, we'll build a polite component that can only

Then we have the implementation of the interface, the @Component
public class DefaultHelloWorld extends AbstractLogEnabled implements HelloWorld, Initializable
Notice the @Component("myCustomHelloWorld")
public class DefaultHelloWorld extends AbstractLogEnabled implements HelloWorld, Initializable
We can go even deeper into more advanced issues and specify multiple component hints for the same implementation, if we want. This can be done as follows:

@Component(hints = {"info", "warning", "error" })
public class DefaultHelloWorld extends AbstractLogEnabled implements HelloWorld, Initializable
This class extends public void initialize() throws InitializationException
{
   // TODO: initialize component
   // getLogger is inherited from AbstractLogEnabled
    getLogger().debug("DefaultHelloWorld initialized");
}
/**
 * Says hello by returning a greeting to the caller.
 *
 * @return A greeting.
 */

public String sayHello()
{
  return "Hello world!";
}
And now, the org.xwiki.component.internal.DefaultHelloWorld

How to find my component and use it?

From other components

To access your component from another component we use the components engine, and specify the dependencies declarative, leaving instantiation and component injection to the be handled by the component manager. The most straightforward way is the use of the requirements mechanism of plexus, specifying that our component is required by the component that needs to access it.

Don't forget that any code that uses the component we wrote needs to have the component interface accessible in its classpath. Even if instantiation and dependency is handled by the engine at runtime, the code still needs to compile. If the two components are not in the same module (the same .jar), don't forget to add the module of the greeter component as a dependency of the module of any component that uses it.

Then, to effectively use the @Component
public class DefaultSocializer extends AbstractLogEnabled implements Socializer, Initializable
{
    
[...]

    
/** Will be injected by the component manager */
    
@Requirement
    
private HelloWorld helloWorld;

    
[...]
}
Note the

The content of org.xwiki.component.internal.DefaultSocializerAnd that's it, you can now use the public class DefaultSocializer extends AbstractLogEnabled implements Socializer, Initializable
{
    [...]

   public void startConversation()
   {
        this.helloWorld.sayHello();
       
        [...]
   }

    [...]
}
More, note that all through the process of defining a communication path between two components, we never referred components implementations, all specifications being done through roles and interfaces: the implementation of a service is completely hidden from any code external to the component.

TODO: refer to the other ways of implementing dependencies but requirements mechanism. Details, explanations, links.

From non-components java code (e.g. older plugins)

For this kind of usages, since we cannot use the component-based architecture advantages and the "magic" of the component manager, the XWiki team has created a helper method that acts like a bridge between component code and non-component code, the

To use our greetings provider component, we simply invoke:

HelloWorld greeter = (HelloWorld) Utils.getComponent(HelloWorld.class);
//use the HelloWorld service
greeter.sayHello();
Note that, even if, in fact, the object returned by this function is an instance of the DefaultHelloWorld, you should never declare your object of the implementation type nor cast to implementation instead of interface. A component is represented by its interface, the implementation for such a service can be provided by any code, any class so relying on the implementation type is neither good practice (since the interface contract should be enough for a component), nor safe. In the future, a maven enforcer plugin will be setup in the build lifecycle, so that any reference to component implementations (located in an "internal" subpackage) will cause build errors.

The usage of 

From wiki pages

In order to use a component in wiki pages, we need to expose it to the scripting environments: groovy and velocity.

Accessing a component from groovy

Since, in groovy, we have access to all classes and functions in XWiki (all this protected by the requirement for programming rights), it means that we can use the same method as in the previous section, using the
<%
def greeter = com.xpn.xwiki.web.Utils.getComponent(org.xwiki.component.HelloWorld.class);
println greeter.sayHello();
%>

TODO: talk about the future plans (?) to make a component accessible in the groovy context through a groovy bridge.

Accessing a component from velocity

XWiki dev team is currently working on the design of a VelocityBridge interface that will handle components access from velocity. Until this specification is ready and its first implementation done, we can do it as follows:
<p/>
We write another component in our package, that implements the <a href="http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-velocity/src/main/java/org/xwiki/velocity/VelocityContextInitializer.java"><tt>VelocityContextInitializer</tt></a>, which is responsible for the initialization of the velocity context in XWiki through its method
@Component("helloWorld")
public class HelloWorldVelocityContextInitializer implements VelocityContextInitializer
{
    
/** The key to add to the velocity context */
    
public static final String VELOCITY_CONTEXT_KEY = "greeter";
   
    
/** The component instance to add to the velocity context, injected by the component manager */
    
@Requirement
    
private HelloWorld helloWorld;

    
/**
     * Add the component instance to the velocity context received as parameter.
     */

    
public void initialize(VelocityContext context)
    
{
        
context.put(VELOCITY_CONTEXT_KEY, this.helloWorld);
    
}
}

The result of this will be the availability of the <p/>
This instance of <p/>
In order to have the
[...]
org.component.internal.vcinitializer.HelloWorldVelocityContextInitializer

Note that this time, we specify a hint for component identification, because we need to differentiate this implementation of the <p/>
Of course, in order to for all this to compile, we need to have the
<dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-velocity</artifactId>
     <version>${platform.core.version}</version>
   </dependency>

And that's it, you have made your
$greeter.sayHello()

For the automatic creation of a velocity accessible xwiki component through this method, we have also created a maven archetype for this purpose too, the xwiki-archetype-velocity-component-1.0-SNAPSHOT.jar. Download it and use it as described in the first part of this tutorial.

How do I find other code?

The XWiki data model

Since the XWiki data model (documents, objects, attachments, etc.) reside in the big, old <p/>
In short, the way this works is based on the fact that implementations for a component don't have to be in the same <p/>
If your component needs to access the XWiki data model, it will use the components from the <p/>
For example:

@Component
public class DefaultHelloWorld implements HelloWorld
{
   /** Provides access to documents. Injected by the Component Manager. */
    @Requirement
   private DocumentAccessBridge documentAccessBridge;

    [...]

   private String getConfiguredGreeting()
    {
       return documentAccessBridge.getProperty("XWiki.XWikiPreferences", "greeting_text");
    }

The XWiki context

Note that the XWiki context is deprecated. It was an older way of keeping track of the current request, which had to be passed around from method to method, looking like a ball and chain present everywhere in the code.
<p/>
In the component world, the current request information is held in an execution context. This is actually more powerful than the old XWiki context, as it is a generic execution context, and you can create one anytime you want and use it anyway you want. And you don't have to manually pass it around with all method calls, as execution contexts are managed by the Execution component, which you can use just like any other XWiki component.
<p/>
In short, if you want to get access to the execution context (which holds context information inserted by the new components), you must declare a requirement on the
/** Provides access to the request context. Injected by the Component Manager. */
    @Requirement
   private Execution execution;

    [...]

   private void workWithTheContext()
    {
       ExecutionContext context = execution.getContext();
       // Do something with the execution context
    }

If you still need to access the old XWiki context, then you can get a reference to it from the execution context, but you should not cast it to an
private void workWithTheContext()
    {
       ExecutionContext context = execution.getContext();
       Map<Object, Object> xwikiContext = (Map<Object, Object>) context.getProperty("xwikicontext");
       // Do something with the XWiki context
    }

If you want not just to use the execution context, but to make something available in every execution context, you can create an implementation of the ExecutionContextInitializer component, and populate newly created execution contexts, just like with velocity contexts.

Code outside components

You can use external libraries as in any other maven module, just declare the right dependencies in your module's <p/>
As a general rule, you should not work with any non-componentized XWiki code, as the way the old code was designed leads to an eventual dependency on the whole <p/>
If you need some functionality from the old core, consider rewriting that part as a new component first, and then use that new component from your code. You should ask first on the devs mailing list, so that we can design and implement it collaboratively.
<p/>
If the effort needed for this is too large, you can try creating a bridge component, by writing just the interfaces in a new module, and make the classes from the core the default implementation of those interfaces. Then, since in the end the xwiki-core, the bridge component and your component will reside in the same classpath, plexus will take care of coupling the right classes. Be careful when writing such bridges, as they are short lived (since in the end all the old code will be replaced by proper components), and if the future real component will have a different interface, then you will have to rewrite your code to adapt to the new method names, or worse, the new component logic.

Deploying and using the component

In order to have your component work with XWiki, build the maven module, and find the produced <b>.jar</b> in the target folder. Copy this .jar to the <b>/WEB-INF/lib</b> folder of your wiki instance, restart the servlet container and you're done: you should be able to access your component from velocity or groovy code, and other potential components depending on it should be able to look it up.

Tags:
   

Get Connected