Wiki source code of Groovy Script Service Tutorial

Last modified by Anca Luca on 2023/02/22 11:17

Show last authors
1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
4
5 {{warning}}
6 This method is a bit convoluted and used to be the only way to achieve this. However, now it's possible to write a script service using the Script Component extension, which would now be the cleaner way to do it. See the documentation on the [[Script Component>>doc:Extension.Script Component.WebHome]] extension page about how to achieve this.
7
8 The general concepts explained on this page remain valid, though.
9 {{/warning}}
10
11 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]].
12
13 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).
14
15 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:
16
17 * When the wiki starts
18 * Whenever the wiki page we will create is modified
19
20 = Prerequisites =
21
22 Before taking this tutorial you should be familiar with:
23
24 * [[Scripting in wiki pages>>doc:xwiki:Documentation.DevGuide.Scripting.WebHome]]
25 * Wiki Components. It's recommended to follow the [[Wiki Component Tutorial>>extensions:Extension.WikiComponent Module]].
26
27 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.
28
29 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! :)
30
31 You will also need programming right to use wiki components and Groovy.
32
33 = The Concept =
34
35 * Create a wiki page and transform it into a Wiki Component
36 * 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.
37 * 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).
38 * To understand why this will work, you need to understand how the Wiki Component feature of XWiki works:
39 ** 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.
40 ** 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.
41
42 One last piece of information: Below we use an Event Listener type of Wiki Component but in practice it could be any Component Role.
43
44 Let's get started!
45
46 = Create a Wiki Component Event Listener =
47
48 * Create a wiki page, e.g. ##HelloWorldGroovyScriptService##
49 * Add an object of class //Wiki Component XWiki Class// ({{code}}XWiki.ComponentClass{{/code}}) to it, with the following properties:
50 ** Role type: {{code}}org.xwiki.observation.EventListener{{/code}}
51 ** Role hint: {{code}}helloWorldGroovyScriptService{{/code}}
52 ** Scope: ##Current Wiki##
53
54 = Turn the Component into an Initializable =
55
56 * Add an object of class //Wiki Component Implements Interface XWiki Class// ({{code}}XWiki.ComponentInterfaceClass{{/code}}) to the page, with the following property:
57 ** Interface qualified name: {{code}}org.xwiki.component.phase.Initializable{{/code}}
58
59 = Add the methods to the component =
60
61 We now need to implements all the methods of the ##EventListener## and ##Initializable## interfaces.
62
63 Some important remarks concerning the ##EventListener## methods:
64
65 * 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.
66 * 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!
67 * Thus we only need to implement the ##getName## (to give it a unique Component hint) and ##getEvents## methods
68
69 Follow these steps:
70
71 * Add an object of class //Wiki Component Method XWiki Class// ({{code}}XWiki.ComponentMethodClass{{/code}}) to the page, with the following properties:
72 ** Name: {{code}}initialize{{/code}}
73 ** Body code:(((
74 {{code language="java"}}
75 {{groovy}}
76 import javax.inject.Named
77 import javax.inject.Singleton
78
79 import org.xwiki.component.annotation.Component
80 import org.xwiki.component.annotation.ComponentAnnotationLoader
81 import org.xwiki.script.service.ScriptService
82
83 @Component
84 @Named("helloWorld")
85 @Singleton
86 public class HelloWorldGroovyScriptService implements ScriptService
87 {
88 public String execute()
89 {
90 return "Hello world from Groovy script service"
91 }
92 }
93
94 // Note: we get the Component Manager for the current wiki since in our example we want to register our Script Service
95 // Component only in the current wiki. We could as well register it in the Root Component Manager for all wikis.
96 def componentManager = services.component.getComponentManager('wiki:' + services.wiki.currentWikiId)
97
98 // Parse the annotations of the class above to generate a Component Descriptor to register the class as a Component in the Component Manager.
99 def loader = new ComponentAnnotationLoader()
100 def descriptors = loader.getComponentsDescriptors(HelloWorldGroovyScriptService.class)
101
102 // Note: Annotations can define several descriptors (by implementing several roles) so we iterate over all of them and register the Component
103 for (descriptor in descriptors) {
104 componentManager.registerComponent(descriptor)
105 }
106 {{/groovy}}
107 {{/code}}
108
109 {{info}}
110 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.
111 {{/info}}
112 )))
113 * 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:
114 ** Name: {{code}}getName{{/code}}
115 ** Body code:(((
116 {{code}}
117 {{groovy}}
118 xcontext.method.output.value = doc.documentReference.toString()
119 {{/groovy}}
120 {{/code}}
121 )))
122 * 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
123 ** Name: {{code}}getEvents{{/code}}
124 ** Body code:(((
125 {{code}}
126 {{groovy}}
127 xcontext.method.output.value = []
128 {{/groovy}}
129 {{/code}}
130 )))
131
132 = Test the script service =
133
134 In a distinct page, enter the code below and make sure that you get the hello world output.
135
136 {{code}}
137 {{velocity}}
138 $services.helloWorld.execute()
139 {{/velocity}}
140 {{/code}}

Get Connected