Wiki source code of Writing XWiki components

Version 28.2 by Vincent Massol on 2010/11/14

Hide last authors
Silvia Macovei 27.1 1 {{box cssClass="floatinginfobox" title="**Contents**"}}{{toc/}}{{/box}}
Anca Luca 1.1 2
Vincent Massol 28.1 3 This tutorial guides you through the creation of an XWiki component, which is a way to extend or customize the XWiki platform. Indeed the XWiki platform is composed of components and it's possible to replace the default implementations with your own implementations. It's also possible to add new component implementations to extend the platform such as by implementing new [[Rendering Macros>>DevGuide.RenderingMacroTutorial]].
Anca Luca 1.1 4
Vincent Massol 28.1 5 {{info}}Components replace the older Plugin architecture which has been deprecated a while ago.{{/info}}
6
Silvia Macovei 26.1 7 You should start by reading the [[Reference document on XWiki Components>>code:Modules.ComponentModule]].
Anca Luca 1.1 8
Silvia Macovei 27.1 9 = Let's get started! =
Anca Luca 1.1 10
11 Enough talking, let's see some code!
12
13 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.
14
Vincent Massol 28.1 15 == Creating a XWiki component using Maven ==
Anca Luca 1.1 16
Vincent Massol 28.1 17 As you've read in the [[XWiki Component Reference>>code:Modules.ComponentModule]] writing a component is a three-streps process (component interface, component implementation, registration of component). To make it easier for you to get started, we have created a maven archetype to help create a simple component module with a single command.
Silvia Macovei 26.1 18
Vincent Massol 28.1 19 After you've [installed Maven>http://dev.xwiki.org/xwiki/bin/view/Community/Building#HInstallingMaven], open a shell prompt an type:
Vincent Massol 28.2 20 {{code language="none"}}
Vincent Massol 28.1 21 mvn archetype:generate -DarchetypeCatalog=http://svn.xwiki.org/svnroot/xwiki/platform/xwiki-tools/trunk/xwiki-archetypes/xwiki-archetype-component/archetype-catalog.xml
Vincent Massol 28.2 22 {{/code}}
Anca Luca 1.1 23
Vincent Massol 28.1 24 Then follow the instructions. For example:
Vincent Massol 28.2 25 {{code language="none"}}
Vincent Massol 28.1 26 vmassol@target $ mvn archetype:generate -DarchetypeCatalog=http://svn.xwiki.org/svnroot/xwiki/platform/xwiki-tools/trunk/xwiki-archetypes/xwiki-archetype-component/archetype-catalog.xml
27 [INFO] Scanning for projects...
28 [INFO]
29 [INFO] ------------------------------------------------------------------------
30 [INFO] Building Maven Stub Project (No POM) 1
31 [INFO] ------------------------------------------------------------------------
32 [INFO]
33 [INFO] >>> maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom >>>
34 [INFO]
35 [INFO] <<< maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom <<<
36 [INFO]
37 [INFO] --- maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom ---
38 [INFO] Generating project in Interactive mode
39 [INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
40 Choose archetype:
41 1: http://svn.xwiki.org/svnroot/xwiki/platform/xwiki-tools/trunk/xwiki-archetypes/xwiki-archetype-component/archetype-catalog.xml -> xwiki-archetype-component (Make it easy to create a maven project for creating a XWiki Component.)
42 Choose a number: : 1
43 Downloading: http://maven.xwiki.org/snapshots/org/xwiki/platform/tools/xwiki-archetype-component/1.0-SNAPSHOT/maven-metadata.xml
44 Downloaded: http://maven.xwiki.org/snapshots/org/xwiki/platform/tools/xwiki-archetype-component/1.0-SNAPSHOT/maven-metadata.xml (383 B at 3.4 KB/sec)
45 Downloading: http://maven.xwiki.org/snapshots/org/xwiki/platform/tools/xwiki-archetype-component/1.0-SNAPSHOT/maven-metadata.xml
46 Downloaded: http://maven.xwiki.org/snapshots/org/xwiki/platform/tools/xwiki-archetype-component/1.0-SNAPSHOT/maven-metadata.xml (383 B at 4.9 KB/sec)
47 Define value for property 'groupId': : com.acme
48 Define value for property 'artifactId': : example
49 Define value for property 'version': 1.0-SNAPSHOT: :
50 Define value for property 'package': com.acme: :
51 Confirm properties configuration:
52 groupId: com.acme
53 artifactId: example
54 version: 1.0-SNAPSHOT
55 package: com.acme
56 Y: : Y
57 [INFO] ----------------------------------------------------------------------------
58 [INFO] Using following parameters for creating project from Old (1.x) Archetype: xwiki-archetype-component:1.0-SNAPSHOT
59 [INFO] ----------------------------------------------------------------------------
60 [INFO] Parameter: groupId, Value: com.acme
61 [INFO] Parameter: packageName, Value: com.acme
62 [INFO] Parameter: package, Value: com.acme
63 [INFO] Parameter: artifactId, Value: example
64 [INFO] Parameter: basedir, Value: /Users/vmassol/dev/xwiki/trunks-clean4/platform/tools/xwiki-archetypes/target
65 [INFO] Parameter: version, Value: 1.0-SNAPSHOT
66 [INFO] ********************* End of debug info from resources from generated POM ***********************
67 [INFO] project created from Old (1.x) Archetype in dir: /Users/vmassol/dev/xwiki/trunks-clean4/platform/tools/xwiki-archetypes/target/example
68 [INFO] ------------------------------------------------------------------------
69 [INFO] BUILD SUCCESS
70 [INFO] ------------------------------------------------------------------------
71 [INFO] Total time: 12.868s
72 [INFO] Finished at: Sun Nov 14 18:27:52 CET 2010
73 [INFO] Final Memory: 9M/81M
74 [INFO] ------------------------------------------------------------------------
75 vmassol@target $
Vincent Massol 28.2 76 {{/code}}
Anca Luca 1.1 77
Vincent Massol 28.2 78 Then go in the created directory (##example## in our example above) and run ##mvn install## to build your component.
Anca Luca 1.1 79
Silvia Macovei 27.1 80 == The component explained ==
81
Silvia Macovei 26.1 82 Assume, for the following explanations, that the package you used is ##org.xwiki.component##
83
Anca Luca 1.1 84 Navigating in the component project folder, you will see standard maven project structure like this:
Silvia Macovei 26.1 85
86 {{code}}
Anca Luca 1.1 87 pom.xml
88 src/main/java/org/xwiki/component/HelloWorld.java
89 src/main/java/org/xwiki/component/internal/DefaultHelloWorld.java
dan 19.1 90 src/main/resources/META-INF/components.txt
Anca Luca 1.1 91 src/test/java/org/xwiki/component/HelloWorldTest.java
Silvia Macovei 26.1 92 {{/code}}
Anca Luca 1.1 93
Silvia Macovei 26.1 94 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.
95
96 If we have a look in the ##pom##, we see something like this:
97
98 {{code}}
99 <groupId>your-group-id</groupId>
Anca Luca 1.1 100 <artifactId>your-artifact-id</artifactId>
101 <version>your-version</version>
Silvia Macovei 26.1 102 {{/code}}
103
Anca Luca 1.1 104 which are the group, artifact and version you used when you created your component
Silvia Macovei 26.1 105
106 {{code}}
107 <properties>
Anca Luca 1.1 108 <!-- TODO: remove this if you inherit a project that has the core version set -->
Anca Luca 17.2 109 <platform.core.version>1.8-SNAPSHOT</platform.core.version>
Anca Luca 1.1 110 </properties>
Silvia Macovei 26.1 111 {{/code}}
Silvia Macovei 27.1 112
Silvia Macovei 26.1 113 It defines the core version for the ##xwiki-component-api## dependency. If your component is created as part of a project already depending on the core (and which most probably already has a property for the core version), use the inherited property value instead of redefining it here. Or, if the component should have the same version as the rest of the XWiki modules, you can use ##${pom.version}## as the version for other XWiki dependencies.
114
115 {{code}}
116 <dependencies>
Anca Luca 1.1 117 <dependency>
118 <groupId>org.xwiki.platform</groupId>
Oana Florea 23.1 119 <artifactId>xwiki-core-component-api</artifactId>
Anca Luca 1.1 120 <version>${platform.core.version}</version>
121 </dependency>
122 <dependency>
123 <groupId>junit</groupId>
124 <artifactId>junit</artifactId>
125 <version>3.8.1</version>
126 <scope>test</scope>
127 </dependency>
128 <!-- Add here all your other dependencies -->
129 </dependencies>
Silvia Macovei 26.1 130 {{/code}}
Silvia Macovei 27.1 131
Silvia Macovei 26.1 132 The code above defines the dependency on the ##xwiki-component module-api## in the core and the junit for the testing phase and, of course, marks the spot for you to add all your other components, modules, and library dependencies for maven to know and put them on your classpath when compiling, etc.
Anca Luca 1.1 133
Silvia Macovei 27.1 134 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:
135
Silvia Macovei 26.1 136 {{code}}
137 <parent>
Oana Florea 23.1 138 <groupId>org.xwiki.platform</groupId>
139 <artifactId>xwiki</artifactId>
140 <version>28</version>
141 </parent>
Silvia Macovei 26.1 142 {{/code}}
Oana Florea 23.1 143
Silvia Macovei 26.1 144 The interface file (##HelloWorld.java##) contains the definition of a regular Java interface, and looks like this:
145
146 {{code}}
dan 19.1 147 @ComponentRole /* annotation used for declaring the service our component will provide */
148 public interface HelloWorld
149 {
150 String sayHello();
151 }
Silvia Macovei 26.1 152 {{/code}}
Anca Luca 1.1 153
Silvia Macovei 26.1 154 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 ##sayHello()##.
Silvia Macovei 27.1 155
Silvia Macovei 26.1 156 Then we have the implementation of the interface, the ##DefaltHelloWorld## class.
Oana Florea 23.1 157
Silvia Macovei 26.1 158 {{code}}
dan 19.2 159 @Component
Anca Luca 1.1 160 public class DefaultHelloWorld extends AbstractLogEnabled implements HelloWorld, Initializable
Silvia Macovei 26.1 161 {{/code}}
dan 19.1 162
Silvia Macovei 26.1 163 Notice the ##@Component## annotation used for declaring the implementation for the component. Optionally, a component implementation can have a //hint// assigned. This is useful especially when we want to distinguish between several implementations for the same type of component. In this case, the ##DefaltHelloWorld## class would look like this:
164
165 {{code}}
dan 19.2 166 @Component("myCustomHelloWorld")
167 public class DefaultHelloWorld extends AbstractLogEnabled implements HelloWorld, Initializable
Silvia Macovei 26.1 168 {{/code}}
Silvia Macovei 27.1 169
dan 20.1 170 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:
Silvia Macovei 26.1 171
172 {{code}}
dan 20.1 173 @Component(hints = {"info", "warning", "error" })
174 public class DefaultHelloWorld extends AbstractLogEnabled implements HelloWorld, Initializable
Silvia Macovei 26.1 175 {{/code}}
dan 19.1 176
Silvia Macovei 26.1 177 This class extends ##AbstractLogEnabled## to be able to use the logging system, implements the component interface, and also the ##Initializable## interface which allows it to hook initialization code upon its instantiation by the component manager, in the ##initialize()## function:
dan 19.2 178
Silvia Macovei 26.1 179 {{code}}
dan 19.1 180 public void initialize() throws InitializationException
181 {
182 // TODO: initialize component
183 // getLogger is inherited from AbstractLogEnabled
184 getLogger().debug("DefaultHelloWorld initialized");
185 }
Silvia Macovei 26.1 186 {{/code}}
Anca Luca 1.1 187
Silvia Macovei 26.1 188 {{code}}
dan 19.1 189 /**
190 * Says hello by returning a greeting to the caller.
191 *
192 * @return A greeting.
193 */
194 public String sayHello()
195 {
196 return "Hello world!";
197 }
Silvia Macovei 26.1 198 {{/code}}
Anca Luca 1.1 199
Silvia Macovei 26.1 200 And now, the ##components.txt## file, in which component implementations present in this jar are specified for the ##ComponentManager## to register them
Anca Luca 1.1 201
Silvia Macovei 26.1 202 {{code}}org.xwiki.component.internal.DefaultHelloWorld{{/code}}
Anca Luca 1.1 203
Silvia Macovei 27.1 204 = How to find my component and use it? =
Anca Luca 24.1 205
Silvia Macovei 27.1 206 == From other components ==
Anca Luca 1.1 207
208 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.
Silvia Macovei 27.1 209
210 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.
211
Silvia Macovei 26.1 212 Then, to effectively use the ##HelloWorld## component, we need a reference to it in the the component that uses it. For this, we use a member variable in the implementation of the using component, for example, a ##Socializer## component will need to be able to say hello to the world:
Anca Luca 1.1 213
Silvia Macovei 26.1 214 {{code}}
dan 19.1 215 @Component
Anca Luca 1.1 216 public class DefaultSocializer extends AbstractLogEnabled implements Socializer, Initializable
217 {
218 [...]
219
220 /** Will be injected by the component manager */
dan 19.1 221 @Requirement
Anca Luca 1.1 222 private HelloWorld helloWorld;
223
224 [...]
225 }
Silvia Macovei 26.1 226 {{/code}}
Anca Luca 1.1 227
Silvia Macovei 26.1 228 Note the ##@Requirement## annotation, which instructs the component manager to inject the required component where needed.
Silvia Macovei 27.1 229
Silvia Macovei 26.1 230 The content of ##components.txt## should be updated with:
dan 19.1 231
Silvia Macovei 26.1 232 {{code}}org.xwiki.component.internal.DefaultSocializer{{/code}}
Anca Luca 1.1 233
Silvia Macovei 26.1 234 And that's it, you can now use the ##helloWorld## member anywhere in the ##DefaultSocializer## class freely, without further concerns, it will be assigned by the component manager provided that the ##HelloWorld## component is on the classpath at runtime when the ##Socializer## is used. Such as:
Anca Luca 1.1 235
Silvia Macovei 26.1 236 {{code}}
Anca Luca 1.1 237 public class DefaultSocializer extends AbstractLogEnabled implements Socializer, Initializable
238 {
239 [...]
240
241 public void startConversation()
242 {
243 this.helloWorld.sayHello();
244
245 [...]
246 }
247
248 [...]
249 }
Silvia Macovei 26.1 250 {{/code}}
Anca Luca 1.1 251
Silvia Macovei 26.1 252 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.
Silvia Macovei 27.1 253
Anca Luca 1.1 254 TODO: refer to the other ways of implementing dependencies but requirements mechanism. Details, explanations, links.
255
Silvia Macovei 27.1 256 == From non-components java code (e.g. older plugins) ==
Anca Luca 1.1 257
Silvia Macovei 26.1 258 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 ##com.xpn.xwiki.web.Utils.getComponent(String role, String hint)## that gets the specified component instance from the component manager and returns it. As seen in the previous sections, the hint is an optional identifier, additional to ##role##, used to differentiate between implementations of the same interface: the //roles// identify services while the hints help differentiate between implementations (see more at [[http://plexus.codehaus.org/guides/developer-guide/building-components/component-identity.html>>http://plexus.codehaus.org/guides/developer-guide/building-components/component-identity.html]]). The ##getComponent## function also has a version without the ##hint## parameter, that uses the default hint.
Silvia Macovei 27.1 259
Silvia Macovei 26.1 260 To use our greetings provider component, we simply invoke:
Anca Luca 1.1 261
Silvia Macovei 26.1 262 {{code}}
dan 19.1 263 HelloWorld greeter = (HelloWorld) Utils.getComponent(HelloWorld.class);
Anca Luca 1.1 264 //use the HelloWorld service
265 greeter.sayHello();
Silvia Macovei 26.1 266 {{/code}}
Anca Luca 1.1 267
Silvia Macovei 26.1 268 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.
Silvia Macovei 27.1 269
Silvia Macovei 26.1 270 {{info}}The usage of ##Utils.getComponent()## functions is highly discouraged, reserved for this type of situations, when you need to access a component from non-componentized code. For the componentized code, you should use either dependency declaration at 'compile-time' (as shown before with annotations) or, if you need to resolve components dependencies at runtime, use the ComponentManager, which you can access by implementing the Composable interface described above.{{/info}}
Anca Luca 1.1 271
Silvia Macovei 27.1 272 == From wiki pages ==
Anca Luca 15.1 273
Anca Luca 1.1 274 In order to use a component in wiki pages, we need to expose it to the scripting environments: groovy and velocity.
275
Silvia Macovei 27.1 276 === Accessing a component from groovy ===
Anca Luca 1.1 277
Silvia Macovei 26.1 278 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>>WritingComponents#HFromnoncomponentsjavacode28egolderplugins29]], using the ##Utils## class. A simple page that would print the greeting from the ##HelloWorld## component would look like this (of course, with your own package for the HelloWorld interface):
Anca Luca 1.1 279
Silvia Macovei 26.1 280 {{code}}
Anca Luca 1.1 281 <%
dan 19.1 282 def greeter = com.xpn.xwiki.web.Utils.getComponent(org.xwiki.component.HelloWorld.class);
Anca Luca 1.1 283 println greeter.sayHello();
284 %>
Silvia Macovei 26.1 285 {{/code}}
Anca Luca 1.1 286
287 TODO: talk about the future plans (?) to make a component accessible in the groovy context through a groovy bridge.
288
Silvia Macovei 27.1 289 === Accessing a component from velocity ===
Anca Luca 1.1 290
291 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:
292
Silvia Macovei 27.1 293 We write another component in our package, that implements the [[##VelocityContextInitializer##>>http://svn.xwiki.org/svnroot/xwiki/platform/core/trunk/xwiki-velocity/src/main/java/org/xwiki/velocity/VelocityContextInitializer.java]], which is responsible for the initialization of the velocity context in XWiki through its method ##initialize(VelocityContext context)##, called automatically when a new velocity context is created. As you probably guessed, we will use this function to add our ##HelloWorld## component to the velocity context.
294
Silvia Macovei 26.1 295 {{code}}
Vincent Massol 21.4 296 @Component("helloWorld")
Anca Luca 1.1 297 public class HelloWorldVelocityContextInitializer implements VelocityContextInitializer
298 {
299 /** The key to add to the velocity context */
300 public static final String VELOCITY_CONTEXT_KEY = "greeter";
301
302 /** The component instance to add to the velocity context, injected by the component manager */
dan 19.2 303 @Requirement
Anca Luca 1.1 304 private HelloWorld helloWorld;
305
306 /**
307 * Add the component instance to the velocity context received as parameter.
308 */
309 public void initialize(VelocityContext context)
310 {
311 context.put(VELOCITY_CONTEXT_KEY, this.helloWorld);
312 }
313 }
Silvia Macovei 26.1 314 {{/code}}
Anca Luca 1.1 315
Silvia Macovei 26.1 316 The result of this will be the availability of the ##HelloWorld## instance in velocity through the key //greeter// (as in ##$greeter.sayHello()##).
Silvia Macovei 27.1 317
Silvia Macovei 26.1 318 This instance of ##HelloWorld## will be injected by the component manager in the ##helloWorld## member, due to the ##@Requirement## annotation.
Silvia Macovei 27.1 319
Silvia Macovei 26.1 320 In order to have the ##VelocityContextInitializer## component accessible to the component manager, therefore initialized indeed when a velocity context is created, we specify it as a component in the ##components.txt## file (in our case, the same file where we have ##DefaultHelloWorld## component):
Anca Luca 1.1 321
Silvia Macovei 26.1 322 {{code}}
Anca Luca 25.1 323 [...]
324 org.component.internal.vcinitializer.HelloWorldVelocityContextInitializer
Silvia Macovei 26.1 325 {{/code}}
Anca Luca 1.1 326
Silvia Macovei 26.1 327 Note that this time, we specify a hint for component identification, because we need to differentiate this implementation of the ##VelocityContextInitializer## from the other implementations, as it is not the only component with this role in XWiki.
Silvia Macovei 27.1 328
Silvia Macovei 26.1 329 Of course, in order to for all this to compile, we need to have the ##VelocityContextInitializer## interface available on the classpath so we have this new dependency in the component module's pom:
Anca Luca 1.1 330
Silvia Macovei 26.1 331 {{code}}
332 <dependency>
Anca Luca 1.1 333 <groupId>org.xwiki.platform</groupId>
334 <artifactId>xwiki-core-velocity</artifactId>
335 <version>${platform.core.version}</version>
336 </dependency>
Silvia Macovei 26.1 337 {{/code}}
Anca Luca 1.1 338
Silvia Macovei 26.1 339 And that's it, you have made your ##HelloWorld## component velocity-accessible! Just recompile your package, copy it in the WEB-INF/lib folder of your xwiki webbapp container, and restart the server. You'll be able to get a greeting in velocity through:
Anca Luca 1.1 340
Silvia Macovei 26.1 341 {{code}}$greeter.sayHello(){{/code}}
Anca Luca 1.1 342
Silvia Macovei 27.1 343 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>>attach:xwiki-archetype-velocity-component-1.0-SNAPSHOT.jar]]. Download it and use it as described in [[the first part of this tutorial>>WritingComponents#HCreatingaXWikicomponentusingmaven]].
Anca Luca 1.1 344
Silvia Macovei 27.1 345 = How do I find other code? =
Anca Luca 1.1 346
Silvia Macovei 27.1 347 == The XWiki data model ==
Anca Luca 1.1 348
Silvia Macovei 26.1 349 Since the XWiki data model (documents, objects, attachments, etc.) reside in the big, old ##xwiki-core## module, and since we don't want to add the whole core and all its dependencies as a dependency of a simple lightweight component (this would eventually lead to a circular dependency, which is not allowed by maven), the current strategy, until the data model is completely turned into a component, is to use a //bridge// between the new component architecture and the old ##xwiki-core##.
Silvia Macovei 27.1 350
Silvia Macovei 26.1 351 In short, the way this works is based on the fact that implementations for a component don't have to be in the same ##.jar## as the interface, and there is no dependency //from// the component interface //to// the actual implementation, only the other way around. So, we made a few simple components that offer basic access to XWiki documents, and declared the classes in ##xwiki-core## as the default implementation for those components.
Silvia Macovei 27.1 352
Silvia Macovei 26.1 353 If your component needs to access the XWiki data model, it will use the components from the ##xwiki-core-bridge## module for that. Note that these interfaces are rather small, so you can't do everything that you could with the old model. If you need to add some methods to the bridge, feel free to propose it on the [[mailing list>>dev:Community.MailingLists]].
Silvia Macovei 27.1 354
Silvia Macovei 26.1 355 For example:
Anca Luca 1.1 356
Silvia Macovei 26.1 357 {{code}}
dan 19.1 358 @Component
Anca Luca 1.1 359 public class DefaultHelloWorld implements HelloWorld
360 {
361 /** Provides access to documents. Injected by the Component Manager. */
dan 19.1 362 @Requirement
Anca Luca 1.1 363 private DocumentAccessBridge documentAccessBridge;
364
365 [...]
366
367 private String getConfiguredGreeting()
368 {
369 return documentAccessBridge.getProperty("XWiki.XWikiPreferences", "greeting_text");
370 }
Silvia Macovei 26.1 371 {{/code}}
Anca Luca 1.1 372
Silvia Macovei 27.1 373 == The XWiki context ==
Anca Luca 1.1 374
Silvia Macovei 26.1 375 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>>http://en.wikipedia.org/wiki/Ball_and_chain]] present everywhere in the code.
Silvia Macovei 27.1 376
Silvia Macovei 26.1 377 In the component world, the current request information is held in an **[[execution context>>http://maven.xwiki.org/site/xwiki-core-parent/xwiki-core-context/apidocs/org/xwiki/context/ExecutionContext.html]]**. 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>>http://maven.xwiki.org/site/xwiki-core-parent/xwiki-core-context/apidocs/org/xwiki/context/Execution.html]]**, which you can use just like any other XWiki component.
Silvia Macovei 27.1 378
Silvia Macovei 26.1 379 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 ##Execution## component (located in the ##xwiki-core-context## module), and then you can write:
Anca Luca 1.1 380
Silvia Macovei 26.1 381 {{code}}
382 /** Provides access to the request context. Injected by the Component Manager. */
dan 19.1 383 @Requirement
Anca Luca 1.1 384 private Execution execution;
385
386 [...]
387
388 private void workWithTheContext()
389 {
390 ExecutionContext context = execution.getContext();
391 // Do something with the execution context
392 }
Silvia Macovei 26.1 393 {{/code}}
Anca Luca 1.1 394
Silvia Macovei 26.1 395 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 ##XWikiContext##, which would pull the whole xwiki-core as a dependency, but to a ##Map##. You won't be able to access all the properties, like the current user name or the URL factory, but you can access anything placed in the internal map of the XWikiContext.
396
397 {{code}}
398 private void workWithTheContext()
Anca Luca 1.1 399 {
400 ExecutionContext context = execution.getContext();
401 Map<Object, Object> xwikiContext = (Map<Object, Object>) context.getProperty("xwikicontext");
402 // Do something with the XWiki context
403 }
Silvia Macovei 26.1 404 {{/code}}
Anca Luca 1.1 405
Silvia Macovei 26.1 406 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>>http://maven.xwiki.org/site/xwiki-core-parent/xwiki-core-context/apidocs/org/xwiki/context/ExecutionContextInitializer.html]] component, and populate newly created execution contexts, just like with [[velocity contexts>>#HAccessingacomponentfromvelocity]].
Anca Luca 1.1 407
Silvia Macovei 27.1 408 == Code outside components ==
Anca Luca 1.1 409
Silvia Macovei 26.1 410 You can use external libraries as in any other maven module, just declare the right dependencies in your module's ##pom.xml##.
Silvia Macovei 27.1 411
Silvia Macovei 26.1 412 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 ##xwiki-core## module, which we are trying to avoid. If the component you are writing is needed by other modules (which is the case with most components, since a component which isn't providing any usable/used services is kind of useless), then this will likely lead to an eventual cyclic dependency, which will break the whole build.
Silvia Macovei 27.1 413
Silvia Macovei 26.1 414 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>>dev:Community.MailingLists]], so that we can design and implement it collaboratively.
Silvia Macovei 27.1 415
Anca Luca 1.1 416 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.
417
Silvia Macovei 27.1 418 = Deploying and using the component =
Anca Luca 1.1 419
Silvia Macovei 27.1 420 In order to have your component work with XWiki, build the maven module, and find the produced **.jar** in the target folder. Copy this .jar to the **/WEB-INF/lib** 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.

Get Connected