Wiki source code of Remote Observation Module
Last modified by Thomas Mortagne on 2023/10/10 14:25
Hide last authors
author | version | line-number | content |
---|---|---|---|
12.1 | 1 | {{box cssClass="floatinginfobox" title="**Contents**"}} | |
2 | {{toc/}} | ||
3 | {{/box}} | ||
1.1 | 4 | ||
5 | Provides the ability to automatically send and receive events through the network. | ||
6 | |||
7 | image:rom.png | ||
8 | |||
9 | = Features = | ||
10 | |||
11 | * api to start/stop channels at runtime | ||
12 | * add custom event converter | ||
13 | * add custom network adapter implementation (default is based on JGroups) | ||
14 | |||
15 | = Quick Example = | ||
16 | |||
17 | == Start/stop channel == | ||
18 | |||
19 | {{code language="java"}} | ||
12.4 | 20 | RemoteObservationManager rom = componentManager.getInstance(RemoteObservationManager.class); | |
1.1 | 21 | ||
22 | // stop the channel with identifier channelid, the channel with configuration file channelid.xml | ||
23 | rom.stopChannel("channelid"); | ||
24 | // start the channel with identifier channelid, the configuration file is releaded | ||
25 | rom.startChannel("channelid"); | ||
26 | {{/code}} | ||
27 | |||
6.1 | 28 | = Setup = | |
29 | |||
30 | == General configuration == | ||
31 | |||
32 | Found in WEB-INF/xwiki.properties file | ||
33 | |||
7.1 | 34 | {{code language="properties"}} | |
6.1 | 35 | #-# [Since 2.0M3] | |
36 | #-# Indicate if the network distribution module is enabled or not. | ||
37 | #-# By default remote events are disabled. | ||
38 | # observation.remote.enabled = false | ||
39 | |||
40 | #-# [Since 2.0M3] | ||
41 | #-# The list of events communication channels to start when the application starts. | ||
42 | #-# By default no channel is configured. | ||
43 | #-# | ||
44 | #-# The default remote event distribution implementation is using JGroups and you'll need to drop your JGroups channel | ||
45 | #-# xml files in the WEB-INF/observation/remote/jgroups/ directory. There's a README file in that directory with more | ||
46 | #-# information. | ||
47 | #-# Example: observation.remote.channels = public, cluster | ||
48 | |||
49 | #-# [Since 2.0M4] | ||
50 | #-# The implementation of network adapter to use. | ||
51 | #-# The default is jgroups. | ||
52 | #-# | ||
53 | #-# By default obnly jgroups is provided. To add one implements NetworkAdaptor componenet interface. The identifier provided in the configuration is matched with the component role hint. | ||
54 | #-# Example: observation.remote.networkadapter = jgroups | ||
55 | {{/code}} | ||
56 | |||
57 | == JGroups == | ||
58 | |||
59 | Currently only JGroups implementation is provided. | ||
60 | |||
6.2 | 61 | The configuration for a channel is searched in the following order: | |
12.1 | 62 | ||
6.2 | 63 | 1. WEB-INF/observation/remote/jgroups/<channel name>.xml | |
64 | 1. <channel name>.xml in the root of jgroups jar file | ||
6.1 | 65 | ||
10.1 | 66 | = Check remote event = | |
67 | |||
68 | Remote Observation Manager is supposed to emulate local event as much as possible but in some case you actually need to do different things depending if it's a remote or local event (invalidate a cache, avvoid doing twice something on the database, etc.). | ||
69 | |||
70 | For this you can use inject the component ##org.xwiki.observation.remote.RemoteObservationManagerContex##. | ||
71 | |||
72 | {{code language="java"}} | ||
73 | import org.xwiki.observation.remote.RemoteObservationManagerContext; | ||
74 | [...] | ||
75 | |||
76 | public class MyListener implements EventListener | ||
77 | { | ||
78 | @Inject | ||
79 | private RemoteObservationManagerContext remoteObservationManagerContext; | ||
80 | |||
81 | [...] | ||
82 | |||
83 | @Override | ||
84 | public void onEvent(Event event, Object source, Object data) | ||
85 | { | ||
86 | if (this.remoteObservationManagerContext.isRemoteState()) { | ||
87 | // do something specific to remote event | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | {{/code}} | ||
92 | |||
11.1 | 93 | From Remote Observation Manager module. | |
94 | |||
95 | {{code language="xml"}} | ||
96 | <dependency> | ||
97 | <groupId>org.xwiki.platform</groupId> | ||
98 | <artifactId>xwiki-platform-observation-remote</artifactId> | ||
99 | <version>${platform.version}</version> | ||
100 | </dependency> | ||
101 | {{/code}} | ||
12.1 | 102 | ||
9.1 | 103 | = Extends Remote Observation Manager = | |
1.1 | 104 | ||
105 | Remote event manage is extendable in many ways depending of the need. | ||
106 | |||
107 | == Add custom event converter == | ||
108 | |||
109 | Each time an event is generated locally or received from network, Remote Event Manager try to converter it to the other side. By default a support of all document events and any fully serializable event is provided but since it's possible to add any kind of local event Remote Event Manager also provide a way to provide converter for any kind of event as well. | ||
110 | |||
12.3 | 111 | There are two kinds of converters: | |
12.1 | 112 | ||
1.1 | 113 | * the local to remote event converter: ##org.xwiki.observation.remote.converter.LocalEventConverter## | |
114 | * the remote to local event converter: ##org.xwiki.observation.remote.converter.RemoteEventConverter## | ||
115 | |||
116 | Most of the time when adding a new event you will need to make conversion both ways. The common way to do this is by extending AbstractEventConverter which provide a default priority and implements both LocalEventConverter and RemoteEventConverter. | ||
117 | |||
118 | {{code language="java"}} | ||
119 | public class ViewEventConverter extends AbstractEventConverter | ||
120 | { | ||
121 | public int getPriority() | ||
122 | { | ||
123 | // default priority level | ||
124 | return 1000; | ||
125 | } | ||
126 | |||
127 | public boolean toRemote(LocalEventData localEvent, RemoteEventData remoteEvent) | ||
128 | { | ||
129 | if (localEvent.getEvent() instanceof ActionExecutionEvent) { | ||
130 | ActionExecutionEvent event = (ActionExecutionEvent) localEvent.getEvent(); | ||
131 | |||
132 | if ("view".equals(event.getActionName())) { | ||
133 | // fill the remote event | ||
134 | remoteEvent.setEvent(event); | ||
135 | } | ||
136 | |||
137 | return true; | ||
138 | } | ||
139 | |||
140 | return false; | ||
141 | } | ||
142 | |||
143 | public boolean fromRemote(RemoteEventData remoteEvent, LocalEventData localEvent) | ||
144 | { | ||
145 | if (localEvent.getEvent() instanceof ActionExecutionEvent) { | ||
146 | ActionExecutionEvent event = (ActionExecutionEvent) localEvent.getEvent(); | ||
147 | |||
148 | if ("view".equals(event.getActionName())) { | ||
149 | // fill the local event | ||
150 | localEvent.setEvent(event); | ||
151 | } | ||
152 | |||
153 | return true; | ||
154 | } | ||
155 | |||
156 | return false; | ||
157 | } | ||
158 | } | ||
159 | {{/code}} | ||
160 | |||
161 | ##toRemote## and ##fromRemote## work the same way: converter manager call them in the priority order one by one and stop when the methods return true. If the converter event data is empty it mean the event should not be sent for remote event or given to Observation Manager for local events. | ||
162 | |||
163 | This converter is a combination local event converter and remote event converter. It's possible to implement just local or just remote. For example one would want to filter some kind of events or document. | ||
164 | |||
165 | == Add custom network adaptor == | ||
166 | |||
167 | The default implementation is based on JGroups, it possible to change just the network communication part easily by providing a new implementation of ##org.xwiki.observation.remote.NetworkAdapter## and setting the role hint of this new component as value of the property ##observation.remote.networkadapter## in the file ##xwiki.properties##. | ||
168 | |||
169 | == Provide custom JGroups listener == | ||
170 | |||
171 | In JGroups based implementation it's possible to provide to set the Receiver to use channel by channel. This is useful for example to make a channel readonly when you want to send events in a public UDP broadcast and not do two ways clustering. | ||
172 | |||
173 | To do this implements the component interface ##org.xwiki.observation.remote.jgroups.JGroupsReceiver## and make the role hint having the same name as the channel. | ||
174 | |||
175 | {{code language="java"}} | ||
12.1 | 176 | @Component | |
177 | @Named("public") | ||
1.1 | 178 | public class PublicJGroupsReceiver implements JGroupsReceiver | |
179 | { | ||
180 | public byte[] getState() | ||
181 | { | ||
182 | return null; | ||
183 | } | ||
184 | |||
185 | public void receive(Message msg) | ||
186 | { | ||
187 | // do nothing, it's a readonly public channel | ||
188 | } | ||
189 | |||
190 | public void setState(byte[] state) | ||
191 | { | ||
192 | |||
193 | } | ||
194 | |||
195 | public void block() | ||
196 | { | ||
197 | |||
198 | } | ||
199 | |||
200 | public void suspect(Address suspectedMbr) | ||
201 | { | ||
202 | |||
203 | } | ||
204 | |||
205 | public void viewAccepted(View newView) | ||
206 | { | ||
207 | |||
208 | } | ||
209 | } | ||
210 | {{/code}} | ||
211 | |||
212 | = JGroups = | ||
213 | |||
214 | See http://www.jgroups.org and http://www.jboss.org/community/wiki/JGroups for more documentation on JGroups and its configuration files. | ||
215 | |||
4.2 | 216 | = Debugging = | |
3.1 | 217 | ||
4.1 | 218 | There are types of logs you can enable to diagnose issues with clustering: | |
12.1 | 219 | ||
4.1 | 220 | * XWiki cluster logs | |
221 | * JGroups cluster logs | ||
3.1 | 222 | ||
14.1 | 223 | For both you'll need to edit the Log4J configuration (see [[xwiki:Documentation.AdminGuide.Logging]]). | |
3.1 | 224 | ||
4.1 | 225 | To enable XWiki cluster logs, add: | |
226 | |||
3.1 | 227 | {{code}} | |
228 | log4j.logger.org.xwiki.observation.remote=trace | ||
229 | {{/code}} | ||
230 | |||
4.1 | 231 | To enable JGroups cluster logs, see the [[JGroups Logging Article>>http://community.jboss.org/wiki/JGroupsLogging]]. | |
232 | |||
1.1 | 233 | = Tutorial = | |
234 | |||
14.1 | 235 | * [[How to setup a cluster of XWiki instance based on distributed events>>xwiki:Documentation.AdminGuide.DistributedEventClusterSetup]]. |