Wiki source code of Writing XWiki components

Version 47.1 by Vincent Massol on 2011/12/06

Hide last authors
Thomas Mortagne 46.1 1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
Anca Luca 1.1 4
Vincent Massol 28.1 5 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 6
Thomas Mortagne 46.1 7 {{info}}
8 Components replace the older Plugin architecture which has been deprecated a while ago.
9 {{/info}}
Vincent Massol 28.1 10
Vincent Massol 37.5 11 You should start by reading the [[Reference document on XWiki Components>>extensions:Extension.Component Module]].
Anca Luca 1.1 12
Silvia Macovei 27.1 13 = Let's get started! =
Anca Luca 1.1 14
15 Enough talking, let's see some code!
16
17 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.
18
Vincent Massol 28.1 19 == Creating a XWiki component using Maven ==
Anca Luca 1.1 20
Vincent Massol 47.1 21 As you've read in the [[XWiki Component Reference>>extensions:Extension.Component Module]] writing a component is a three-streps process (component interface, component implementation, registration of component).
Silvia Macovei 26.1 22
Vincent Massol 47.1 23 To make it easier for you to get started, we have created a [[Maven Archetype>>http://maven.apache.org/plugins/maven-archetype-plugin/]] to help create a simple component module with a single command.
Sergiu Dumitriu 37.2 24
Vincent Massol 47.1 25 After you've [[installed Maven>>http://maven.apache.org]], open a shell prompt an type:
Vincent Massol 28.2 26 {{code language="none"}}
Vincent Massol 47.1 27 mvn archetype:generate
Vincent Massol 28.2 28 {{/code}}
Anca Luca 1.1 29
Vincent Massol 47.1 30 This will list all archetypes available on Maven Central. If instead you wish to directly use the XWiki Component Archetype, you can directly type (update the version to use the version you wish to use):
31
32 {{code language="none"}}
33 mvn archetype:generate \
34 -DarchetypeArtifactId=xwiki-commons-component-archetype \
35 -DarchetypeGroupId=org.xwiki.commons \
36 -DarchetypeVersion=3.3
37 {{/code}}
38
Vincent Massol 28.1 39 Then follow the instructions. For example:
Sergiu Dumitriu 37.2 40
Vincent Massol 28.2 41 {{code language="none"}}
Vincent Massol 47.1 42 vmassol@tmp $ mvn archetype:generate
Vincent Massol 28.1 43 [INFO] Scanning for projects...
44 [INFO]
45 [INFO] ------------------------------------------------------------------------
46 [INFO] Building Maven Stub Project (No POM) 1
47 [INFO] ------------------------------------------------------------------------
48 [INFO]
Vincent Massol 47.1 49 [INFO] >>> maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom >>>
Vincent Massol 28.1 50 [INFO]
Vincent Massol 47.1 51 [INFO] <<< maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom <<<
Vincent Massol 28.1 52 [INFO]
Vincent Massol 47.1 53 [INFO] --- maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom ---
Vincent Massol 28.1 54 [INFO] Generating project in Interactive mode
55 [INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
56 Choose archetype:
Vincent Massol 47.1 57 ...
58 493: remote -> org.xwiki.commons:xwiki-commons-component-archetype (Make it easy to create a maven project for creating XWiki Components.)
59 ...
60 Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 152: 493
61 Choose org.xwiki.commons:xwiki-commons-component-archetype version:
62 1: 3.3-milestone-1
63 2: 3.3-milestone-2
64 3: 3.3-SNAPSHOT
65 Choose a number: 3:
Vincent Massol 28.1 66 Define value for property 'groupId': : com.acme
67 Define value for property 'artifactId': : example
68 Define value for property 'version': 1.0-SNAPSHOT: :
69 Define value for property 'package': com.acme: :
70 Confirm properties configuration:
71 groupId: com.acme
72 artifactId: example
73 version: 1.0-SNAPSHOT
74 package: com.acme
75 Y: : Y
76 [INFO] ----------------------------------------------------------------------------
Vincent Massol 47.1 77 [INFO] Using following parameters for creating project from Archetype: xwiki-commons-component-archetype:3.3-SNAPSHOT
Vincent Massol 28.1 78 [INFO] ----------------------------------------------------------------------------
79 [INFO] Parameter: groupId, Value: com.acme
Vincent Massol 47.1 80 [INFO] Parameter: artifactId, Value: example
81 [INFO] Parameter: version, Value: 1.0-SNAPSHOT
Vincent Massol 28.1 82 [INFO] Parameter: package, Value: com.acme
Vincent Massol 47.1 83 [INFO] Parameter: packageInPathFormat, Value: com/acme
84 [INFO] Parameter: package, Value: com.acme
85 [INFO] Parameter: version, Value: 1.0-SNAPSHOT
86 [INFO] Parameter: groupId, Value: com.acme
Vincent Massol 28.1 87 [INFO] Parameter: artifactId, Value: example
Vincent Massol 47.1 88 [INFO] project created from Archetype in dir: /private/tmp/example
Vincent Massol 28.1 89 [INFO] ------------------------------------------------------------------------
90 [INFO] BUILD SUCCESS
91 [INFO] ------------------------------------------------------------------------
Vincent Massol 47.1 92 [INFO] Total time: 7:58.355s
93 [INFO] Finished at: Tue Dec 06 17:18:00 CET 2011
94 [INFO] Final Memory: 7M/81M
Vincent Massol 28.1 95 [INFO] ------------------------------------------------------------------------
Vincent Massol 28.2 96 {{/code}}
Anca Luca 1.1 97
Vincent Massol 28.2 98 Then go in the created directory (##example## in our example above) and run ##mvn install## to build your component.
Anca Luca 1.1 99
Vincent Massol 29.1 100 == The Component explained ==
Silvia Macovei 27.1 101
Vincent Massol 29.1 102 Assume, for the following explanations, that the package you used is ##com.acme##
Silvia Macovei 26.1 103
Vincent Massol 29.1 104 Navigating in the component project folder, you will see the following standard Maven project structure:
Silvia Macovei 26.1 105
Vincent Massol 30.1 106 {{code language="none"}}
Anca Luca 1.1 107 pom.xml
Vincent Massol 29.1 108 src/main/java/com/acme/HelloWorld.java
109 src/main/java/com/acme/internal/DefaultHelloWorld.java
Vincent Massol 33.1 110 src/main/java/com/acme/internal/HelloWorldScriptService.java
dan 19.1 111 src/main/resources/META-INF/components.txt
Vincent Massol 29.1 112 src/test/java/com/acme/HelloWorldTest.java
Silvia Macovei 26.1 113 {{/code}}
Anca Luca 1.1 114
Vincent Massol 33.1 115 which correspond to the default files created: the ##HelloWorld## interface (a.k.a component role), its implementation ##DefaultHelloWorld## (component implementation), a test class for this component ##HelloWorldTest##, the component declaration file ##components.txt## and the Maven project ##pom.xml## file. The ##HelloWorldScriptService## file is described below when we explain how to make the component's API available to wiki pages.
Silvia Macovei 26.1 116
Vincent Massol 29.1 117 If you have a look in the ##pom.xml##, you'll notice the following dependencies:
Silvia Macovei 26.1 118
Vincent Massol 30.1 119 {{code language="xml"}}
Vincent Massol 29.1 120 <dependencies>
Anca Luca 1.1 121 <dependency>
Vincent Massol 44.3 122 <groupId>org.xwiki.commons</groupId>
Thomas Mortagne 45.1 123 <artifactId>xwiki-commons-component-api</artifactId>
Vincent Massol 44.3 124 <version>${commons.version}</version>
Anca Luca 1.1 125 </dependency>
Vincent Massol 29.1 126 <!-- Testing dependencies -->
Anca Luca 1.1 127 <dependency>
Vincent Massol 44.3 128 <groupId>org.xwiki.commons</groupId>
129 <artifactId>xwiki-commons-test</artifactId>
130 <version>${commons.version}</version>
Anca Luca 1.1 131 <scope>test</scope>
Vincent Massol 29.1 132 </dependency>
Anca Luca 1.1 133 </dependencies>
Silvia Macovei 26.1 134 {{/code}}
Silvia Macovei 27.1 135
Thomas Mortagne 46.1 136 The code above defines the dependency on the ##xwiki-core-component-api## in the core which is where XWiki Component notions are defined. There's also a dependency on ##xwiki-core-shared-tests## which provides helper classes to easily test components.
Anca Luca 1.1 137
Silvia Macovei 26.1 138 The interface file (##HelloWorld.java##) contains the definition of a regular Java interface, and looks like this:
139
Vincent Massol 30.1 140 {{code language="java"}}
Vincent Massol 29.1 141 @ComponentRole /* annotation used for declaring the service our component provides */
dan 19.1 142 public interface HelloWorld
143 {
144 String sayHello();
145 }
Silvia Macovei 26.1 146 {{/code}}
Anca Luca 1.1 147
Vincent Massol 29.1 148 Keep in mind that this interface specifies the API that other components can use on your component. In our case, we'll build a polite component that can ##sayHello()##.
Silvia Macovei 27.1 149
Sorin Burjan 42.3 150 Then we have the implementation of the interface, the ##DefaultHelloWorld## class.
Oana Florea 23.1 151
Thomas Mortagne 46.1 152 {{code language="java"}}
Vincent Massol 29.1 153 @Component /* annotation used for declaring a component implementation */
Vincent Massol 44.1 154 @Singleton /* annotation used for defining the component as a singleton */
Vincent Massol 29.1 155 public class DefaultHelloWorld implements HelloWorld
Silvia Macovei 26.1 156 {{/code}}
dan 19.1 157
Vincent Massol 44.1 158 Note that optionally, there is a ##@Named## annotation to specify a component //hint//. This is useful especially when we want to distinguish between several implementations for the same type of component. Image we had a special HelloWorld implementation taking the greeting message from a database; it could look like:
Silvia Macovei 26.1 159
Thomas Mortagne 46.1 160 {{code language="java"}}
Vincent Massol 44.1 161 @Component
162 @Named("database")
Vincent Massol 29.1 163 public class DatabaseHelloWorld implements HelloWorld
Silvia Macovei 26.1 164 {{/code}}
Silvia Macovei 27.1 165
Vincent Massol 29.1 166 Then the ##sayHello## in ##DefaultHelloWorld## is basic in this example:
Silvia Macovei 26.1 167
Vincent Massol 30.1 168 {{code language="java"}}
dan 19.1 169 /**
170 * Says hello by returning a greeting to the caller.
171 *
172 * @return A greeting.
173 */
174 public String sayHello()
175 {
176 return "Hello world!";
177 }
Silvia Macovei 26.1 178 {{/code}}
Anca Luca 1.1 179
Vincent Massol 29.1 180 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 181
Thomas Mortagne 46.1 182 {{code language="none"}}
183 com.acme.internal.DefaultHelloWorld
184 {{/code}}
Anca Luca 1.1 185
Silvia Macovei 27.1 186 = How to find my component and use it? =
Anca Luca 24.1 187
Silvia Macovei 27.1 188 == From other components ==
Anca Luca 1.1 189
Vincent Massol 30.1 190 To access your component from another component we use the components engine, and specify the dependencies, leaving instantiation and component injection to the be handled by the component manager.
Silvia Macovei 27.1 191
Vincent Massol 30.1 192 In order to use the ##HelloWorld## component, you need a reference to it in the the component that uses it. For this, you should 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:
Silvia Macovei 27.1 193
Silvia Macovei 26.1 194 {{code}}
dan 19.1 195 @Component
Vincent Massol 44.1 196 @Singleton
Vincent Massol 30.1 197 public class DefaultSocializer implements Socializer
Anca Luca 1.1 198 {
199 [...]
200
201 /** Will be injected by the component manager */
Vincent Massol 44.1 202 @Inject
Anca Luca 1.1 203 private HelloWorld helloWorld;
204
205 [...]
206 }
Silvia Macovei 26.1 207 {{/code}}
Anca Luca 1.1 208
Vincent Massol 44.1 209 Note the ##@Inject## annotation, which instructs the component manager to inject the required component where needed.
Silvia Macovei 27.1 210
Silvia Macovei 26.1 211 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 212
Silvia Macovei 26.1 213 {{code}}
Vincent Massol 30.1 214 public class DefaultSocializer implements Socializer
Anca Luca 1.1 215 {
216 [...]
217
218 public void startConversation()
219 {
220 this.helloWorld.sayHello();
221
222 [...]
223 }
224
225 [...]
226 }
Silvia Macovei 26.1 227 {{/code}}
Anca Luca 1.1 228
Silvia Macovei 26.1 229 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 230
231 == From non-components java code (e.g. older plugins) ==
Anca Luca 1.1 232
Vincent Massol 31.1 233 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. The ##getComponent## function also has a signature without the ##hint## parameter, that uses the default hint.
Vincent Massol 30.2 234
Vincent Massol 31.1 235 To use our greetings provider component, we would simply invoke:
Silvia Macovei 27.1 236
Silvia Macovei 26.1 237 {{code}}
Vincent Massol 31.1 238 HelloWorld greeter = Utils.getComponent(HelloWorld.class);
Anca Luca 1.1 239 greeter.sayHello();
Silvia Macovei 26.1 240 {{/code}}
Anca Luca 1.1 241
Silvia Macovei 26.1 242 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 243
Thomas Mortagne 46.1 244 {{info}}
245 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 as described in the [[Component Module Reference>>extensions:Extension.Component Module]].
246 {{/info}}
Anca Luca 1.1 247
Silvia Macovei 27.1 248 == From wiki pages ==
Anca Luca 15.1 249
Vincent Massol 32.1 250 Components can be made accessible to wiki pages by writing a ##ScriptService## implementation. They can then be access using any provided scripting language (velocity, groovy, python, ruby, php, etc).
Anca Luca 1.1 251
Vincent Massol 32.1 252 Let's make our ##sayHello## method accessible:
253
254 {{code language="java"}}
Vincent Massol 44.1 255 @Component
256 @Named("hello")
257 @Singleton
Vincent Massol 32.1 258 public class HelloWorldScriptService implements ScriptService
259 {
Vincent Massol 44.1 260 @Inject
Vincent Massol 32.1 261 private HelloWorld helloWorld;
262
263 public String greet()
264 {
265 return this.helloWorld.sayHello();
266 }
267 }
268 {{/code}}
269
270 Notice the component hint used (the ##hello## part in the ##@Component##). This is the name under which the script service will be accessible from scripting languages.
271
272 For example to access it in velocity you'd write:
Thomas Mortagne 46.1 273 {{code language="none"}}$services.hello.greet(){{/code}}
Vincent Massol 32.1 274
275 From Groovy:
Thomas Mortagne 46.1 276 {{code language="none"}}services.hello.greet(){{/code}}
Vincent Massol 32.1 277
Vincent Massol 34.1 278 Now for our script service to work we need to register it as a component and thus add it to the ##META-INF/components.txt## file:
Thomas Mortagne 46.1 279 {{code language="none"}}...
280 com.acme.internal.HelloWorldScriptService{{/code}}
Vincent Massol 32.1 281
Vincent Massol 33.1 282 We also need to make the Script Service infrastructure available in our classpath. This is done by adding the following in your ##pom.xml## file:
Thomas Mortagne 46.1 283 {{code language="xml"}}<dependency>
Vincent Massol 44.3 284 <groupId>org.xwiki.commons</groupId>
285 <artifactId>xwiki-commons-script</artifactId>
286 <version>${commons.version}</version>
Thomas Mortagne 46.1 287 </dependency>{{/code}}
Vincent Massol 33.1 288
Vincent Massol 35.1 289 = Accessing Legacy code =
Anca Luca 1.1 290
Vincent Massol 35.1 291 By legacy we mean old XWiki code that hasn't been moved to components yet.
Anca Luca 1.1 292
Silvia Macovei 27.1 293 == The XWiki data model ==
Anca Luca 1.1 294
Silvia Macovei 26.1 295 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 296
Silvia Macovei 26.1 297 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 298
Silvia Macovei 26.1 299 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 300
Silvia Macovei 26.1 301 For example:
Anca Luca 1.1 302
Silvia Macovei 26.1 303 {{code}}
dan 19.1 304 @Component
Vincent Massol 44.1 305 @Singleton
Anca Luca 1.1 306 public class DefaultHelloWorld implements HelloWorld
307 {
308 /** Provides access to documents. Injected by the Component Manager. */
Vincent Massol 44.1 309 @Inject
Anca Luca 1.1 310 private DocumentAccessBridge documentAccessBridge;
311
312 [...]
313
314 private String getConfiguredGreeting()
315 {
316 return documentAccessBridge.getProperty("XWiki.XWikiPreferences", "greeting_text");
317 }
Silvia Macovei 26.1 318 {{/code}}
Anca Luca 1.1 319
Silvia Macovei 27.1 320 == The XWiki context ==
Anca Luca 1.1 321
Silvia Macovei 26.1 322 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 323
Silvia Macovei 26.1 324 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 325
Vincent Massol 44.2 326 In short, if you want to get access to the execution context (which holds context information inserted by the new components), you must declare an injection point on the ##Execution## component (located in the ##xwiki-core-context## module), and then you can write:
Anca Luca 1.1 327
Silvia Macovei 26.1 328 {{code}}
329 /** Provides access to the request context. Injected by the Component Manager. */
Vincent Massol 44.1 330 @Inject
Anca Luca 1.1 331 private Execution execution;
332
333 [...]
334
335 private void workWithTheContext()
336 {
337 ExecutionContext context = execution.getContext();
338 // Do something with the execution context
339 }
Silvia Macovei 26.1 340 {{/code}}
Anca Luca 1.1 341
Silvia Macovei 26.1 342 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.
343
344 {{code}}
345 private void workWithTheContext()
Anca Luca 1.1 346 {
347 ExecutionContext context = execution.getContext();
348 Map<Object, Object> xwikiContext = (Map<Object, Object>) context.getProperty("xwikicontext");
349 // Do something with the XWiki context
350 }
Silvia Macovei 26.1 351 {{/code}}
Anca Luca 1.1 352
Silvia Macovei 26.1 353 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 354
Silvia Macovei 27.1 355 == Code outside components ==
Anca Luca 1.1 356
Silvia Macovei 26.1 357 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 358
Silvia Macovei 26.1 359 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 360
Silvia Macovei 26.1 361 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 362
Anca Luca 1.1 363 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.
364
Vincent Massol 35.1 365 = Deploying the Component =
Anca Luca 1.1 366
Vincent Massol 32.1 367 Now that we have a functioning Component let's build it and deploy it to a XWiki Enterprise instance:
Thomas Mortagne 46.1 368
Vincent Massol 32.1 369 * To build the component, issue ##mvn install##. This generates a JAR in the ##target## directory of your project.
370 * To install it into a XWiki Enterprise instance, just copy that JAR file in ##XE_WAR_HOME/WEB-INF/lib## where ##XE_WAR_HOME## is where the XWiki Enterprise WAR is deployed.
371
372 Your component is now ready for service.
373
374 Enjoy!

Get Connected