<?xml version="1.0" encoding="UTF-8" standalone="yes"?><extensionVersion xmlns="http://www.xwiki.org/extension"><id>org.xwiki.platform:xwiki-platform-component-wiki</id><name>XWiki Platform - Component - Wiki Components</name><type>jar</type><rating><totalVotes>0</totalVotes><averageVote>0.0</averageVote></rating><summary>Make it possible to implement a component in a wiki page, using wiki objects</summary><description>== Introduction ==

Introduced in XWiki 4.2, the wiki component module is a bridge between [[XWiki java components&gt;&gt;xwiki:Documentation.DevGuide.WritingComponents]] and wiki documents. The module has 3 features:
1. Write components directly within documents, using XObjects. Those compoments will be considered similar to components written in Java by the rest of the platform.
1. Easily bind a java component to a document through a mechanism re-instantiating the java component each time the corresponding document is modified. This allows the component to rely on information or even scripts stored in the wiki.
1. Instantiate components directly through XObjects.

{{info}}
A more recent alternative which bring much better performances and reliability is to use [[Script Components&gt;&gt;extensions:Extension.Script Component]].
{{/info}}

== Write components in documents ==

{{info}}
Programming rights are required in order to write components in wiki documents
{{/info}}

It is possible to write components in wiki documents, using XObjects. This is not the preferred way to write a component but this mechanism can be used to make experiments on a running XWiki instance for example. Four different XClasses allow to define components:

; XWiki.ComponentClass
: Allows to mark that the document holds a component
; XWiki.ComponentMethodClass
: Allows to implement component methods with wiki syntax
; XWiki.ComponentDependencyClass
: Allows to have other components injected in the context when components methods are executed
; XWiki.ComponentInterfaceClass
: Allows to implement other interfaces in addition to the component interface

=== Defining the component ===

First we need to choose a [[Component role&gt;&gt;http://platform.xwiki.org/xwiki/bin/view/DevGuide/WritingComponents#HTheComponentexplained]] to implement, in this tutorial we will implement an [[Event Listener&gt;&gt;https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-core/xwiki-commons-observation/xwiki-commons-observation-api/src/main/java/org/xwiki/observation/EventListener.java]], which allows to execute code after some events are fired.

; **Component Role Type**
: The ##Role## (Interface) the component implements, in our example **//org.xwiki.observation.EventListener//**

{{info}}
When we refer to Role Types, they can be:
* Simple types, for example: ##org.xwiki.query.QueryManager##
* Parameterized types, like: ##org.xwiki.model.reference.EntityReferenceSerializer&lt;java.lang.String&gt;##
{{/info}}
; **Component Role Hint**
: The ##Hint## of your component, it must allow to identify it, in our example **//helloworld//**
; **Component Scope**
: The ##Scope## of your component, it can be registered at 3 different level: only for the current wiki (default value), global (for a whole wiki farm) or only for the current user (the user who wrote the component)

This is what you should have:

{{image reference="Listener1.png" /}}

If you look at the logs you should see the following error:

{{code language="none"}}
org.xwiki.component.wiki.WikiComponentRuntimeException: You need to add an Object of type [XWiki.ComponentMethodClass] in 
document [xwiki:Main.Listener] to implement method [org.xwiki.observation.EventListener.getName]
{{/code}}

This is normal since we haven't implemented the component methods, yet.

=== Implementing methods ===

To implement methods we need to add one XWiki.ComponentMethodClass XObject per method. For an [[Event Listener&gt;&gt;https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-core/xwiki-commons-observation/xwiki-commons-observation-api/src/main/java/org/xwiki/observation/EventListener.java]] we need to implement ##getEvents()##, ##getName()## and onEvent(Event, Object, Object)##

We will implement those methods using XWiki Syntax, which allows us to use [[scripting languages such as velocity or groovy&gt;&gt;http://platform.xwiki.org/xwiki/bin/view/DevGuide/Scripting]].

To interact with the outside world the methods are provided with some special binding variables grouped under the "method" context:
1. **xcontext.method.input**, a Map&lt;Integer, Object&gt; of the arguments passed to the implemented method
1. **xcontext.method.output**, an Object that you must be set if the implemented method returns a value
1. **xcontext.method.component**, a reference to **this** component (for calling a method of it from another one)
1. **xcontext.method.&lt;dependency binding name&gt;**, reference to inject dependencies (see "Adding dependencies" below)

Here's the code we need to implement our [[Event Listener&gt;&gt;https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-core/xwiki-commons-observation/xwiki-commons-observation-api/src/main/java/org/xwiki/observation/EventListener.java]]:

* Method: **getEvents** (((

{{code language="none"}}
{{groovy}}
import org.xwiki.bridge.event.*

xcontext.method.output.value = [new DocumentCreatedEvent(), new DocumentUpdatedEvent()]
{{/groovy}}
{{/code}}
)))
* Method: **onEvent** (((

{{code language="none"}}
{{groovy}}
System.out.println("Hello World ! The document ${xcontext.method.input.get(1)} has been created/modified.")
{{/groovy}}
{{/code}}
)))
* Method: **getName** (((

{{code language="none"}}
{{groovy}}
xcontext.method.output.value = "helloworld"
{{/groovy}}
{{/code}}
)))

This is what you should have: 

{{image reference="Listener2.png" /}}

You now have a component implemented within a wiki page, but if you look at the log you should see something similar to this:

{{code language="none"}}
WARN  .o.i.DefaultObservationManager - The [$Proxy47] listener has overwritten a previously registered listener [$Proxy42] 
since they both are registered under the same id [helloworld]. In the future consider removing a Listener first if you really want to register it again.
{{/code}}

That's normal, every time you save the document XWiki tries to register the component as an Event Listener, but it needs to remove the previous one first, there's something we can do about that.

=== Adding dependencies ===

Like Java components our Wiki components can declare dependencies, those dependencies are injected in the method context (xcontext.method). In our current example we need to retrieve the [[Observation Manager&gt;&gt;https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-core/xwiki-commons-observation/xwiki-commons-observation-api/src/main/java/org/xwiki/observation/ObservationManager.java]] to be able to un-register ourselves from it. 

To do that, add a XWiki.ComponentDependencyClass XObject to your document and fill the object with the following information:

; **Dependency Role Type**
: The ##Role## (Interface) the component implements, in our example **//org.xwiki.observation.ObservationManager//**
; **Dependency Role Hint**
: The ##Hint## of the dependency, in our example **//default//**
; **Binding name**
: The name of the variable that will be put in our context to access the component, in our example **//observationManager//**

This is what you should have: 

{{image reference="Listener3.png" /}}

We'll now use that from a new method, see below.

=== Implementing other interfaces ===

To implement another interface, add a XWiki.ComponentInterfaceClass XObject to your document and fill the object to fit our needs. Here we will use this to implement [[org.xwiki.component.phase.Disposable&gt;&gt;https://github.com/xwiki/xwiki-commons/blob/master/xwiki-commons-core/xwiki-commons-component/xwiki-commons-component-api/src/main/java/org/xwiki/component/phase/Disposable.java]]. This will allow us to unregister the listener when the component is unloaded.

This is what you should have:

{{image reference="Listener4.png" /}}

Now, since we're implementing this, we need to implement the only method from this interface, **##dispose()##**:

{{code language="none"}}
{{groovy}}
System.out.println("Hello world listener unregistered, it will now be registered again")
xcontext.method.observationManager.removeListener("helloworld")
{{/groovy}}
{{/code}}

This is what it should look like:

{{image reference="Listener5.png" /}}

And this time we're done, our Listener will print a line every time a document is created or modified in the wiki, and every time you'll save the document holding the component you should see this in the log:

{{code language="none"}}
Hello world listener unregistered, it will now be registered again
{{/code}}

You can download the complete example: [[Main.Listener.xar&gt;&gt;attach:Main.Listener.xar]].

In case you need to perform some logging from such a WikiComponent, the [[logging ScriptService&gt;&gt;Extension.Logging Module||anchor="HScripting"]] can be used to do so.

== Bind component implementations to documents ==

When implementations of a component are defined (or at least of part of them) in documents what we did was:

* Search for all the implementations within the wiki, usually through a [[query&gt;&gt;extensions:Extension.Query Module]] looking for XObjects of a specific XClass. 
* Create a component descriptor for each implementation found
* Register each implementation
* Set up a listener, to listen to:
** document creations and modifications, to unregister and register the implementations they contain, if any
** document deletions, to unregister implementations they contain, if any

The wiki component modules removes the need for some of those steps, to use it your component role must extend the ##WikiComponent## Interface 

{{code language="java"}}
/**
 * Represents the definition of a wiki component implementation. A java component can extend this interface if it needs
 * to be bound to a document, in order to be unregistered and registered again when the document is modified, and
 * unregistered when the document is deleted.
 * 
 * @version $Id: 406ebb4a913d7bbe9cb5f2297152c9afd9efc9a2 $
 * @since 4.2M3
 */
public interface WikiComponent
{
    /**
     * Get the reference of the document this component instance is bound to.
     *
     * @return the reference to the document holding this wiki component definition.
     */
    DocumentReference getDocumentReference();
    
    /**
     * @return the role implemented by this component implementation.
     */
    public Type getRoleType();

    /**
     * @return the hint of the role implemented by this component implementation.
     */
    String getRoleHint();
}
{{/code}}

[[View on github&gt;&gt;https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki/src/main/java/org/xwiki/component/wiki/WikiComponent.java]]

Once your component extends the Interface above you need to provide a component builder implementing the following interface:

{{code language="java"}}
/**
 * Allows to provide a list of documents holding one or more {@link WikiComponent}, and to build components from those
 * documents.
 *
 * @version $Id: 9d1ae83dc1970909a991ccf0a216809c0ddee30a $
 * @since 4.2M3
 */
@Role
public interface WikiComponentBuilder
{
    /**
     * Get the list of documents holding components.
     *
     * @return the list of documents holding components
     */
    List&lt;DocumentReference&gt; getDocumentReferences();

    /**
     * Build the components defined in a document XObjects. Being able to define more than one component in a document
     * depends on the implementation. It is up to the implementation to determine if the last author of the document
     * has the required permissions to register a component.
     * 
     * @param reference the reference to the document that holds component definition objects
     * @return the constructed component definition
     * @throws WikiComponentException when the document contains invalid component definition(s)
     */
    List&lt;WikiComponent&gt; buildComponents(DocumentReference reference) throws WikiComponentException;
}
{{/code}}

[[View on github&gt;&gt;https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki/src/main/java/org/xwiki/component/wiki/WikiComponentBuilder.java]]

When this is done, every time a document holding information about one of your component implementations is modified ###buildComponents(DocumentReference)## will be called, allowing you to rebuild the component bound to it. You will find an example of implementation below.

=== Example of WikiComponent implementation ===

We will make a very simple component for this example, Proverb. As seen above, this component Role extends WikiComponent.

{{code language="java"}}
package org.xwiki.example;

import org.xwiki.component.annotation.Role;
import org.xwiki.component.wiki.WikiComponent;

@Role
public interface Proverb extends WikiComponent
{
    /**
     * @return A proverb
     */
    String get();
}
{{/code}}

We write a class implementing this new Role, this will be the **bridge** between components and the wiki:

{{code language="java"}}
package org.xwiki.example.internal;

import java.lang.reflect.Type;

import org.xwiki.component.wiki.WikiComponentScope;
import org.xwiki.example.Proverb;
import org.xwiki.model.reference.DocumentReference;

public class WikiProverb implements Proverb
{
    private DocumentReference reference;

    private DocumentReference authorReference;

    private String proverb;

    private String hint;

    public WikiProverb(DocumentReference reference, DocumentReference authorReference, String hint, String proverb)
    {
        this.reference = reference;
        this.authorReference = reference;
        this.hint = hint;
        this.proverb = proverb;
    }

    @Override
    public String get()
    {
        return proverb;
    }

    @Override
    public DocumentReference getDocumentReference()
    {
        return reference;
    }

    @Override
    public DocumentReference getAuthorReference()
    {
        return authorReference;
    }

    @Override
    public Type getRoleType()
    {
        return Proverb.class;
    }

    @Override
    public String getRoleHint()
    {
        return hint;
    }

    @Override
    public WikiComponentScope getScope()
    {
        return WikiComponentScope.WIKI;
    }
}
{{/code}}

And now we need a builder:

{{code language="java"}}
package org.xwiki.example.internal;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.component.wiki.WikiComponent;
import org.xwiki.component.wiki.WikiComponentBuilder;
import org.xwiki.component.wiki.WikiComponentException;
import org.xwiki.context.Execution;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.query.Query;
import org.xwiki.query.QueryManager;

import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;

@Component
@Singleton
@Named("proverb")
public class WikiProverbBuilder implements WikiComponentBuilder
{
    @Inject
    private Execution execution;

    @Inject
    private QueryManager queryManager;

    @Inject
    private EntityReferenceSerializer&lt;String&gt; serializer;

    @Override
    public List&lt;DocumentReference&gt; getDocumentReferences()
    {
        List&lt;DocumentReference&gt; references = new ArrayList&lt;DocumentReference&gt;();

        try {
            Query query =
                queryManager.createQuery("select doc.space, doc.name from Document doc, doc.object(XWiki.Proverb) "
                    + "as proverb where proverb.proverb &lt;&gt; ''",
                    Query.XWQL);
            List&lt;Object[]&gt; results = query.execute();
            for (Object[] result : results) {
                references.add(
                    new DocumentReference(getXWikiContext().getDatabase(), (String) result[0], (String) result[1]));
            }
        } catch (Exception e) {
            // Fail "silently"
            e.printStackTrace();
        }

        return references;
    }

    @Override
    public List&lt;WikiComponent&gt; buildComponents(DocumentReference reference) throws WikiComponentException
    {
        List&lt;WikiComponent&gt; components = new ArrayList&lt;WikiComponent&gt;();
        DocumentReference proverbXClass = new DocumentReference(getXWikiContext().getDatabase(), "XWiki", "Proverb");

        try {
            XWikiDocument doc = getXWikiContext().getWiki().getDocument(reference, getXWikiContext());

            if (!getXWikiContext().getWiki().getRightService().hasAccessLevel("admin", doc.getAuthor(),
                "XWiki.XWikiPreferences", getXWikiContext())) {
                throw new WikiComponentException(String.format("Failed to building Proverb components from document "
                    +" [%s], author [%s] doesn't have admin rights in the wiki", reference.toString(),
                    doc.getAuthor()));
            }

            for (BaseObject obj : doc.getXObjects(proverbXClass)) {
                String roleHint = serializer.serialize(obj.getReference());
                components.add(new WikiProverb(reference, doc.getAuthorReference(), roleHint,
                    obj.getStringValue("proverb")));
            }
        } catch (Exception e) {
            throw new WikiComponentException(String.format("Failed to build Proverb components from document [%s]",
                reference.toString()), e);
        }

        return components;
    }

    private XWikiContext getXWikiContext()
    {
        return (XWikiContext) this.execution.getContext().getProperty("xwikicontext");
    }
}
{{/code}}

Voila! Once the JAR is dropped within xwiki/WEB-INF/lib/ it's possible to create components from the wiki, to do so you need to:

1. Create the XWiki.Proverb class, by adding a ##String## property named ##proverb## to it (((

{{image reference="WikiComponents-Step1.png" /}}
)))
1. Create objects from that class, in one or multiple documents (((

{{image reference="WikiComponents-Step2.png" /}}
)))
1. You can now write a little script to retrieve your components (((

{{code language="none"}}
{{groovy}}

import org.xwiki.example.Proverb;

for (Proverb proverb : services.component.getComponentManager().getInstanceList(Proverb.class)) {
  println("* " + proverb.get())
}

{{/groovy}}
{{/code}}

)))
1. You'll see the proverbs you created appear (((

{{image reference="WikiComponents-Step3.png" /}}
)))

=== A real-life example ===

A real life example of this can be found in the [[UI Extension module&gt;&gt;Extension.UIExtension Module]]:
* The component : [[UIExtension&gt;&gt;https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-uiextension/xwiki-platform-uiextension-api/src/main/java/org/xwiki/uiextension/UIExtension.java]]
* The "bridge" implementation : [[WikiUIExtension&gt;&gt;https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-uiextension/xwiki-platform-uiextension-api/src/main/java/org/xwiki/uiextension/internal/WikiUIExtension.java]]
* The builder : [[WikiUIExtensionComponentBuilder&gt;&gt;https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-uiextension/xwiki-platform-uiextension-api/src/main/java/org/xwiki/uiextension/internal/WikiUIExtensionComponentBuilder.java]]

== Instantiate components in documents ==

Since [[XWiki 9.5RC1&gt;&gt;xwiki:ReleaseNotes.Data.XWiki.9\.5RC1]], the Wiki Components API offers a new {{scm  path="xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki/src/main/java/org/xwiki/component/wiki/WikiObjectComponentBuilder.java"}}WikiObjectComponentBuilder{{/scm}} interface that can be used to allow custom XObjects to instantiate different components implementing the {{scm  path="xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki/src/main/java/org/xwiki/component/wiki/WikiComponent.java"}}WikiComponent{{/scm}} interface.

Components that can be instantiated out of XObjects are directly registered against the Component Manager.

=== Allow a new component to be instantiated through XObjects ===

In order to use this API, you firstly have to create a new component with the WikiObjectComponentBuilder role. The hint of this component should be defined as the path of the XClass that you want to use with this builder. Every time an XObject implementing the specified XClass is added, updated, or deleted from the wiki, the builder will be in charge of extracting the necessary informations it needs from the XObject and instantiating one or more WikiComponent. The returned WikiComponent(s) are then registered against the Component Manager using a scope that is defined in each instantiated component through {{scm  path="xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki/src/main/java/org/xwiki/component/wiki/WikiComponent.java"}}WikiComponent#getScope{{/scm}}.

Note that :

* As it’s the builder responsibility to instantiate the correct WikiComponent(s), it should also be in charge to determine if the XObject author has the sufficient rights to instantiate such components, security checks should then be made on the builder side.
* By specifying a local or an absolute XClass path in the hint of your WikiObjectComponentBuilder, you can allow the components to be built out of XObjects present in a single Wiki (with an absolute path) or out of every XObjects implementing this particular XClass in the farm (when using a local path).</description><licenses><name>GNU Lesser General Public License 2.1</name></licenses><website>http://extensions.xwiki.org/xwiki/bin/view/Extension/WikiComponent%20Module</website><authors><name>XWiki Development Team</name><url>https://xwiki.org/xwiki/bin/view/XWiki/XWikiTeam</url></authors><scm><connection><system>git</system><path>git://github.com/xwiki/xwiki-platform.git/xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki</path></connection><developerConnection><system>git</system><path>git@github.com:xwiki/xwiki-platform.git/xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki</path></developerConnection><url>https://github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-component/xwiki-platform-component-wiki/</url></scm><issueManagement><system>jira</system><url>https://jira.xwiki.org/browse/XWIKI</url></issueManagement><recommended>false</recommended><properties><key>maven.groupid</key><stringValue>org.xwiki.platform</stringValue></properties><properties><key>maven.artifactid</key><stringValue>xwiki-platform-component-wiki</stringValue></properties><properties><key>maven.Model</key><stringValue>org.xwiki.platform:xwiki-platform-component-wiki:jar:18.6.0</stringValue></properties><properties><key>xwiki.extension.recommendedVersions.commons</key><stringValue>org.xwiki.commons:.*/[18.6.0]</stringValue></properties><properties><key>xwiki.extension.recommendedVersions.platform</key><stringValue>org.xwiki.commons:.*/[18.6.0],
      org.xwiki.rendering:.*/[18.6.0],
      org.xwiki.platform:.*/[18.6.0]</stringValue></properties><properties><key>xwiki.extension.recommendedVersions</key><stringValue>org.xwiki.commons:.*/[18.6.0],
      org.xwiki.rendering:.*/[18.6.0],
      org.xwiki.platform:.*/[18.6.0]</stringValue></properties><version>4.2-milestone-3</version><repositories><id>maven-xwiki</id><uri>https://nexus.xwiki.org/nexus/content/groups/public</uri><type>maven</type></repositories></extensionVersion>