Show last authors
1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
4
5 A [[Script Service>>doc:Extension.Script Module.WebHome]] is a [[XWiki Components>>doc:Extension.Component Module]] and the recommended approach for writing [[XWiki Components>>doc:Extension.Component Module]] is either to [[write them in Java>>doc:xwiki:Documentation.DevGuide.Tutorials.WritingComponents.WebHome]] or to [[write them in wiki pages using Wiki Components>>extensions:Extension.WikiComponent Module]].
6
7 However, there's currently a [[known limitation>>https://jira.xwiki.org/browse/XWIKI-16117]] in the Wiki Component feature that prevents using it for writing Script Service components. That should leave you with the Java option which is the recommended approach. However, for various reasons, you may not want to use Java and you may still want to write them in wiki pages (some don't like Java or don't want to go in Java development or want to have a fast turnaround time, etc).
8
9 You'll learn below how to develop a Script Service, in a wiki page, using Groovy. However, **the technique demonstrated will work for any scripting language and not just for Script Service. It's a way to be able to execute any script once in your wiki and be guaranteed that whenever your wiki restarts, your script also executes!** More precisely the script will execute:
10
11 * When the wiki starts
12 * Whenever the wiki page we will create is modified
13
14 = Prerequisites =
15
16 Before taking this tutorial you should be familiar with:
17
18 * [[Scripting in wiki pages>>doc:xwiki:Documentation.DevGuide.Scripting.WebHome]]
19 * Wiki Components. It's recommended to follow the [[Wiki Component Tutorial>>extensions:Extension.WikiComponent Module]].
20
21 It's also better if you have some understanding of [[XWiki Components>>doc:Extension.Component Module]] and [[Event Listeners>>doc:xwiki:Documentation.DevGuide.Tutorials.WritingEventListenerTutorial.WebHome]] but it's not mandatory.
22
23 Generally speaking, the approach below is quite complex and requires a good level of understanding of the XWiki internals. Not for the faint of hearts! :)
24
25 You will also need programming right to use wiki components and Groovy.
26
27 = The Concept =
28
29 * Create a wiki page and transform it into a Wiki Component
30 * This Component will only serve as a way to define our Script Service or execute our script. It won't be used for anything else.
31 * The trick is that we'll have this Component implement the ##Initializable## interface (and thus implement an ##initialize()## method which is where we will define our Script Service or execute our script).
32 * To understand why this will work, you need to understand how the Wiki Component feature of XWiki works:
33 ** When a page containing a wiki component is created or modified, a Java component is created dynamically and registered against XWiki's Component Manager. To do this, the Component is instantiated (using a Java Dynamic Proxy) and this instance is registered. When a Component is instantiated, if it implements the ##Initializable## interface, its ##initialize()## method is called.
34 ** When the wiki starts, the Wiki Component feature is initialized and it looks for all wiki pages defining wiki components and registers them. And thus it instantiates the Components, and thus our ##initialize()## method is also called.
35
36 One last piece of information: Below we use an Event Listener type of Wiki Component but in practice it could be any Component Role.
37
38 Let's get started!
39
40 = Create a Wiki Component Event Listener =
41
42 * Create a wiki page, e.g. ##HelloWorldGroovyScriptService##
43 * Add an object of class //Wiki Component XWiki Class// ({{code}}XWiki.ComponentClass{{/code}}) to it, with the following properties:
44 ** Role type: {{code}}org.xwiki.observation.EventListener{{/code}}
45 ** Role hint: {{code}}helloWorldGroovyScriptService{{/code}}
46 ** Scope: ##Current Wiki##
47
48 = Turn the Component into an Initializable =
49
50 * Add an object of class //Wiki Component Implements Interface XWiki Class// ({{code}}XWiki.ComponentInterfaceClass{{/code}}) to the page, with the following property:
51 ** Interface qualified name: {{code}}org.xwiki.component.phase.Initializable{{/code}}
52
53 = Add the methods to the component =
54
55 We now need to implements all the methods of the ##EventListener## and ##Initializable## interfaces.
56
57 Some important remarks concerning the ##EventListener## methods:
58
59 * Since the Event Listener won't be used as a real listener, we don't want it to listen to anything at all, which is why we return an empty array for its ##getEvents## method.
60 * Since the Event Listener won't listen to any event, we don't even need to implement the ##onEvent## method that Event Listeners are supposed to implement!
61 * Thus we only need to implement the ##getName## (to give it a unique Component hint) and ##getEvents## methods
62
63 Follow these steps:
64
65 * Add an object of class //Wiki Component Method XWiki Class// ({{code}}XWiki.ComponentMethodClass{{/code}}) to the page, with the following properties:
66 ** Name: {{code}}initialize{{/code}}
67 ** Body code:(((
68 {{code language="java"}}
69 {{groovy}}
70 import javax.inject.Named
71 import javax.inject.Singleton
72
73 import org.xwiki.component.annotation.Component
74 import org.xwiki.component.annotation.ComponentAnnotationLoader
75 import org.xwiki.script.service.ScriptService
76
77 @Component
78 @Named("helloWorld")
79 @Singleton
80 public class HelloWorldGroovyScriptService implements ScriptService
81 {
82 public String execute()
83 {
84 return "Hello world from Groovy script service"
85 }
86 }
87
88 // Note: we get the Component Manager for the current wiki since in our example we want to register our Script Service
89 // Component only in the current wiki. We could as well register it in the Root Component Manager for all wikis.
90 def componentManager = services.component.getComponentManager('wiki:' + services.wiki.currentWikiId)
91
92 // Parse the annotations of the class above to generate a Component Descriptor to register the class as a Component in the Component Manager.
93 def loader = new ComponentAnnotationLoader()
94 def descriptors = loader.getComponentsDescriptors(HelloWorldGroovyScriptService.class)
95
96 // Note: Annotations can define several descriptors (by implementing several roles) so we iterate over all of them and register the Component
97 for (descriptor in descriptors) {
98 componentManager.registerComponent(descriptor)
99 }
100 {{/groovy}}
101 {{/code}}
102
103 {{info}}
104 You could of course use any script here in any supported Scripting language. You could also use an [[##~~{~~{include/}}## macro>>doc:Extension.Include Macro]] macro to let the script execution read code from another page.
105 {{/info}}
106 )))
107 * Add the following method to the component, to make sure the listener has a unique name, e.g. the current page reference converted to a string:
108 ** Name: {{code}}getName{{/code}}
109 ** Body code:(((
110 {{code}}
111 {{groovy}}
112 xcontext.method.output.value = doc.documentReference.toString()
113 {{/groovy}}
114 {{/code}}
115 )))
116 * Add the method below to the component. As we discussed we don't want to listen to any events, and we return an empty array
117 ** Name: {{code}}getEvents{{/code}}
118 ** Body code:(((
119 {{code}}
120 {{groovy}}
121 xcontext.method.output.value = []
122 {{/groovy}}
123 {{/code}}
124 )))
125
126 = Test the script service =
127
128 In a distinct page, enter the code below and make sure that you get the hello world output.
129
130 {{code}}
131 {{velocity}}
132 $services.helloWorld.execute()
133 {{/velocity}}
134 {{/code}}

Get Connected