Component Module

The Component Module allows adding Java extensions to the XWiki Platform. The XWiki architecture is based on Component-oriented development and it is currently implementing its own lightweight ComponentManager.

Component Module Features

The Component Module defines the following features:

  • Supports JSR330 annotations for declaring Component Requirements: Component interfaces, Component implementations and Component dependencies.
  • The ability to have Singleton Components and Per-lookup Components which means that a new instance is created when the component is retrieved.
  • The ability to define a Hint in order to separate different Components implementations that are implementing the same Component interface.
  • Automatic Field-based injection of Component Dependencies and support for List and Map injections.
  • Ability for Components to perform some initialization when they are instantiated.
  • Component logging.
  • Notification of Component Events when a new Component is registered or unregistered in the system.
  • Ability to define Component Realms which means isolating groups of components.
  • Ability to define JSR330 Providers as Components and to have them injected automatically.

Register a Component

A Component can be registered:

  • By setting Java Annotations on the Component Interface and the Component implementation, then declaring the Component implementation in a META-INF/components.txt file.
  • By programmatically registering the Component against the Component Manager instance.

Component Annotations

The available Component Annotations are:

  • Role is used for declaring an Interface as a Component Role.
  • Component is used for declaring a class implementing a Component Interface as a Component implementation.
  • ComponentRole is now marked as deprecated and replaced by @Role.
  • InstantiationStrategy is used for declaring a Component implementation as being a Singleton or not. As seen in the ComponentInstantiationStrategy source page, the available strategies are:
    • Singleton - The same component implementation instance is returned for all lookups. When specifying a Singleton component, the @Singleton annotation should be used instead. 
    • PER_LOOKUP - A new component implementation instance is created at a each lookup. In this case, you should still use the @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP) annotation. If not specified, the default is currently a Singleton.
  • Inject is used for declaring a field which requires a Component implementation to be injected at runtime.
  • Named is used to give a Hint to the Role to specify which implementation should be injected.

Examples

Simple Registration

Define the Macro interface as the Component Interface:

@Role
public interface Macro
{
    List<Block> execute();
}

Define the MessageMacro class as a Macro with a message Hint in order to differentiate it from other implementations:

@Component
@Named("message")
@Singleton
public class MessageMacro implements Macro
{
  @Inject
  private Execution execution;

  @Inject
  @Named("box")
  private Macro boxMacro;

  public List<Block> execute()
  {
     //
  }
}

If you want to use the default hint, you can leave it empty. The MessageMacro has 2 components injected:

  • An Execution implementation with a default Hint
  • A Macro implementation with the box Hint

The implementation injected will be found at runtime by the Component Manager. In order for the MessageMacro component to be available at runtime, you need to list it with its fully-qualified name in a META-INF/components.txt file as follows: org.xwiki.rendering.internal.macro.message.MessageMacro

Registration Using a Provider

Providers are registered as standard components with the following annotations:

  • @Component
  • @Named
  • @Singleton 

The main difference, is that in this case there is no more need for the @Role annotation because implementing the javax.inject.Provider interface should suffice. In the below example, the ConfiguredQueryExecutorProvider Provider returns instances of QueryExecutor:

@Component
@Singleton
public class ConfiguredQueryExecutorProvider implements Provider<QueryExecutor>
{
...
  @Override
  public QueryExecutor get()
  {
      // return instance
  }
}

To use this provider, you need to get it injected as follows:

@Inject
private Provider<QueryExecutor> queryExecutorProvider;

The main advantages of using Providers include:

  • the ability to break a cyclic dependency.
  • the ability to get a component instance without having it injected when the Component is looked up for the first time; this allows your code to easily react to the registration of new Components at runtime.
  • the ability to control how to return the instance you are providing for.
A custom Provider doesn't need to be registered in order to be able to get it injected. If no registered Provider is found, the Component Manager injects a default Provider which does a lookup of the Component Role it provides, using the Component Manager. 

Registration Using Multiple Hints

It is possible to register a Component multiple times, using different Hints. One use case would be to register the MessageMacro presented above for the Hints: info, warning and error:

@Component(hints = {"info", "warning", "error" })
@Singleton
public class MessageMacro implements Macro {

//...

}

Registration Using Multiple Roles

A component can be registered with multiple Roles, each one implementing another interface and producing a different instance. However, it is recommended to separate roles implementations in different classes as much as possible.

@Component
@Singleton
@Named("multiplerolesmacro")
public class MessageMacro implements Macro, EventListener {

//...

}

List and Map Injections

In order to have all the Component implementations of a given Component Interface injected, you need to have a field of type List 

@Inject
private List<Macro> macros;

or Map defined, where Map keys are Hint values:

@Inject
private Map<String, Macro> macros;

Access the Component Manager

For the cases when you don't know at compile time what you want injected, you cannot use automatic dependency injection anymore. However, you can inject the ComponentManager in which the component is registered:

@Inject
private ComponentManager componentManager;

You can also access the root ComponentManager

@Inject
@Named("root")
private ComponentManager componentManager;

or even the context ComponentManager:

@Inject
@Named("context")
private Provider<ComponentManager> componentManagerProvider;

The Context Component Manager is a proxy Component Manager allowing to access a different Component Manager depending on the context.

The Component Manager

The Component Manager class allows to lookup components and register new components programmatically. Also, there is only one instance of the root Component Manager in the system, the default hint.

The Component Manager initialization is done automatically by the Container Module. However, if you are using XWiki as a library in your own application and you need to initialize the Component Manager in order to be able to perform lookups of other Components, you can use the below code:

EmbeddableComponentManager componentManager = new EmbeddableComponentManager();
componentManager.initialize(this.getClass().getClassLoader());

To free all resources that components of this manager might hold, use the dispose() method:componentManager.dispose();

Initialize a Component

In case the Component implementation needs to perform an initialization, it has to implement the org.xwiki.component.phase.Initializable interface. This way, when the component is instantiated, the initialize() method will be called:

@Component
@Singleton
public class DefaultObservationManager implements ObservationManager, Initializable
{
  public void initialize() throws InitializationException
  {
      // Initialization code
  }
}

Log a Component

To enable the Component logging, you can automatically inject a SLF4J Logger:

import org.slf4j.Logger;
...
@Component
@Singleton
public class DefaultObservationManager implements ObservationManager
{
  @Inject
  private Logger logger;

  public void myMethod()
  {
      this.logger.info("Info message");
      // ...
  }
}

 

Related Pages

Search this space

 

Most popular tags

Failed to execute the [groovy] macro
  1. access rights
  2. activity stream
  3. annotation
  4. attachment
  5. comment
  6. Document Tree Macro
  7. export
  8. Extension Manager
  9. Flamingo skin
  10. global user
  11. Groovy event listener
  12. group
  13. nested page
  14. search
  15. skin
  16. syntax
  17. user
  18. user profile
  19. velocity macros
  20. wiki
  21. wysiwyg
  22. XWiki Applications
  23. xwikiattachment_archive table
  24. xwikiattachment table
  25. xwikiattrecyclebin table
  26. xwikiproperties table

[Display all tags from this space]