Wiki source code of Component Module

Version 7.2 by Silvia Macovei on 2009/10/29 10:38

Show last authors
1 {{box cssClass="floatinginfobox" title="**Contents**"}}{{toc start="2" depth="4"/}}{{/box}}
2
3 = Component Module =
4
5 [[XWiki's Architecture>>platform:DevGuide.Architecture]] is based on [[Component-oriented Development>>http://en.wikipedia.org/wiki/Component-based_software_engineering]] (see also this [[Plexus Component Tutorial>>http://plexus.codehaus.org/ref/why-use-components.html]] to understand the benefits of using Components).
6
7 There are several Component Manager solutions out there for Java. To name a few:
8
9 * [[Guice>>http://code.google.com/p/google-guice/]]
10 * [[OSGi>>http://www.osgi.org/]]
11 * [[JSR291>>http://jcp.org/en/jsr/detail?id=291]], [[JSR277>>http://jcp.org/en/jsr/detail?id=277]], [[JSR294>>http://jcp.org/en/jsr/detail?id=294]], [[Project Jigsaw>>http://openjdk.java.net/projects/jigsaw/]]
12
13 XWiki has chosen to be independent of all existing Components Managers and instead to define some simple Component interfaces that can then be bound on any existing Component Manager. XWiki is currently implementing its own [[lightweight Component Manager>>http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-component/xwiki-component-default/src/main/java/org/xwiki/component/embed/EmbeddableComponentManager.java]] with the idea to implement a [[Guice>>http://code.google.com/p/google-guice/]] bridge as soon as Guice starts implementing [[JSR299>>http://jcp.org/en/jsr/detail?id=299]]/[[JSR330>>http://jcp.org/en/jsr/detail?id=330]] annotations.
14
15 == Features ==
16
17 The Component Module defines the following features:
18
19 * Annotations to declare Component interfaces, Component implementations and Component dependencies (a.k.a as Component Requirements). Note that as soon as [[JSR299>>http://jcp.org/en/jsr/detail?id=299]]/[[JSR330>>http://jcp.org/en/jsr/detail?id=330]] become official we'll drop our annotations and use these instead.
20 * Ability to have Singleton Components and Per-lookup Components (a new instance is created when the component is retrieved).
21 * Ability to define a Hint to separate different Components implementations implementing the same Component interface.
22 * Automatic Field-based injection of Component Dependencies. Support for List and Map injections.
23 * Ability for Components to perform some initialization when they are instantiated.
24 * Ability for Components to log things.
25 * Component Events to be notified when a new Component is registered/unregistered in the system.
26 * [Future] Ability to define Component Realms, i.e. the ability to isolate groups of components.
27
28 == Component Registration ==
29
30 There are two ways to register a Component:
31
32 * By setting Java Annotations on the Component Interface and the Component implementation and declaring the Component implementation in a ##META-INF/components.txt## file.
33 * By programatically registering the Component against the Component Manager instance.
34
35 === Using Annotations ===
36
37 The following Annotations are available:
38
39 * ##[[ComponentRole>>http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-component/xwiki-component-api/src/main/java/org/xwiki/component/annotation/ComponentRole.java]]##: Used to declare an Interface as a Component Interface (a.k.a Role)
40 * ##[[Component>>http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-component/xwiki-component-api/src/main/java/org/xwiki/component/annotation/Component.java]]##: Used to declare a class implementing a Component Interface as a Component implementation
41 * ##[[InstantiationStrategy>>http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-component/xwiki-component-api/src/main/java/org/xwiki/component/annotation/InstantiationStrategy.java]]##: Used to declare a Component implementation as being a singleton or not
42 * ##[[Requirement>>http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-component/xwiki-component-api/src/main/java/org/xwiki/component/annotation/Requirement.java]]##: Used to declare a field as requiring a Component implementation to be injected at runtime
43
44 Here's a quick example:
45
46 {{code language="java"}}
47 @ComponentRole
48 public interface Macro
49 {
50 List<Block> execute();
51 }
52 {{/code}}
53
54 {{code language="java"}}
55 @Component("message")
56 public class MessageMacro implements Macro
57 {
58 @Requirement
59 private Execution execution;
60
61 @Requirement("box")
62 private Macro boxMacro;
63
64 public List<Block> execute()
65 {
66 ...
67 }
68 }
69 {{/code}}
70
71 In this example:
72
73 * The ##Macro## interface is the Component Interface
74 * The ##MessageMacro## class is declared as a Macro with a ##message## Hint (to differentiate it from other implementations). Note that you can leave the hint empty, in which case it'll be the default hint.
75 * The ##MessageMacro## needs 2 other components injected: ##Execution## and ##Macro##. The implementation injected will be found at runtime by the Component Manager. An ##Execution## implementation with a default Hint will be injected and a ##Macro## implementation with the ##box## Hint will be injected.
76
77 {{info}}
78 A Component Interface + a Hint must be unique across the system. If you have 2 Components registered with the same Component Interface and the same Hint then the Component Manager will only register one of them and a warning will be printed in the logs. That's unless you really want this and have [[defined an override>>#override]].
79 {{/info}}
80
81 In addition, for our ##MessageMacro## component to be available at runtime, you need to list it with its fully-qualified name in a ##META-INF/components.txt## file:
82
83 {{code language="none"}}
84 org.xwiki.rendering.internal.macro.message.MessageMacro
85 {{/code}}
86
87 ==== Registering a Component with several Hints ====
88
89 You can register a component several times, for different hints. For example, to register our ##MessageMacro## for the 3 hints ##info##, ##error##, ##warning## we could use:
90
91 {{code language="java"}}
92 @Component(hints = {"info", "warning", "error" })
93 public class MessageMacro implements Macro
94 ...
95 {{/code}}
96
97 ==== List and Map injections ====
98
99 You may want to have all the Component implementations of a given Component Interface injected. To do this you simply need to have a field of type List or Map defined.
100
101 For example to have all ##Macro## implementations injected you'd use:
102
103 {{code language="java"}}
104 @Requirement
105 private List<Macro> macros;
106 {{/code}}
107
108 or
109
110 {{code language="java"}}
111 @Requirement
112 private Map<String, Macro> macros;
113 {{/code}}
114
115 In the second example the Map keys are the Hint values.
116
117 ==== Getting access to the Component Manager ====
118
119 Automatic dependency injection is great and easy, but there are times when you don't know at compile time what you want injected. For these situations you can inject the ##ComponentManager##. For example:
120
121 {{code language="java"}}
122 @Requirement
123 private ComponentManager componentManager;
124 {{/code}}
125
126 See below for the API available on ##ComponentManager##.
127
128 {{id name="override"/}}
129
130 ==== Overrides ====
131
132 Sometimes you'll have several JARs with Component implementations for the Component Interface and same Hint. In this case you need to tell the Component Manager which implementation to use. This is done by creating a ##META-INF/component-overrides.txt## file and listing the implementation to use (using the same format as for the ##components.txt## file).
133
134 == Component Manager ==
135
136 The Component Manager is a key class when using Components. It allows you to lookup components and register new components programatically. Here's the API it offers:
137
138 {{code language="java"}}
139 <T> boolean hasComponent(Class<T> role);
140 <T> boolean hasComponent(Class<T> role, String roleHint);
141 <T> T lookup(Class<T> role) throws ComponentLookupException;
142 <T> T lookup(Class<T> role, String roleHint) throws ComponentLookupException;
143 <T> void release(T component) throws ComponentLifecycleException;
144 <T> Map<String, T> lookupMap(Class<T> role) throws ComponentLookupException;
145 <T> List<T> lookupList(Class<T> role) throws ComponentLookupException;
146 <T> void registerComponent(ComponentDescriptor<T> componentDescriptor) throws ComponentRepositoryException;
147 <T> void registerComponent(ComponentDescriptor<T> componentDescriptor, T componentInstance) throws ComponentRepositoryException;
148 void unregisterComponent(Class< ? > role, String roleHint);
149 <T> ComponentDescriptor<T> getComponentDescriptor(Class<T> role, String roleHint);
150 <T> List<ComponentDescriptor<T>> getComponentDescriptorList(Class<T> role);
151 {{/code}}
152
153 There's only one instance of the Component Manager in the system.
154
155 Note that when you're writing a Component you normally don't even need to have access to it since all you need to do is declare dependencies using Annotations as explained above.
156
157 == Component Initialization ==
158
159 If your Component implementation needs to perform some initialization, you'll need to make it implement the ##org.xwiki.component.phase.Initializable## interface. For example:
160
161 {{code language="java"}}
162 @Component
163 public class DefaultObservationManager implements ObservationManager, Initializable
164 {
165 ...
166
167 public void initialize() throws InitializationException
168 {
169 // Perform some init here.
170 }
171 }
172 {{/code}}
173
174 You are then guaranteed that when your component is instantiated its ##initialize()## method will be called.
175
176 {{info}}
177 Using a constructor doesn't always work since you might need dependency injections to be done prior to the initialization happening.
178 {{/info}}
179
180 == Component Logging ==
181
182 If your Component implementation needs to log something it'll need to implement the ##org.xwiki.component.phase.LogEnabled## interface. However in order to make it even easier we're providing a ##org.xwiki.component.logging.AbstractLogEnabled## class that your component can simply extend.
183
184 For example:
185
186 {{code language="java"}}
187 @Component
188 public class DefaultObservationManager extends AbstractLogEnabled implements ObservationManager
189 {
190 public void doSomething()
191 {
192 getLogger().info("Some info level logging");
193 ...
194 }
195 }
196 {{/code}}
197
198 See the [[Logger interface>>http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-component/xwiki-component-api/src/main/java/org/xwiki/component/logging/Logger.java]] for more details on the logging API.
199
200 == Tutorial ==
201
202 See the [["Writing a XWiki Component" tutorial>>platform:DevGuide.WritingComponents]].

Get Connected