1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
|
---
title: Vaadin IPC for Liferay
order: 6
layout: page
---
[[portal.liferay-ipc]]
= Vaadin IPC for Liferay
((("IPC add-on", id="term.portal.liferay-ipc", range="startofrange")))
Portlets rarely live alone. A page can contain multiple portlets and when the
user interacts with one portlet, you may need to have the other portlets react
to the change immediately. This is not normally possible with Vaadin portlets,
as Vaadin applications need to get an Ajax request from the client-side to
change their user interface. On the other hand, the regular inter-portlet
communication (IPC) mechanism in Portlet 2.0 Specification requires a complete
page reload, but that is not appropriate with Vaadin or in general Ajax
applications, which do not require a page reload. One solution is to communicate
between the portlets on the server-side and then use a server-push mechanism to
update the client-side.
The Vaadin IPC for Liferay Add-on takes another approach by communicating
between the portlets through the client-side. Events (messages) are sent through
the [classname]#LiferayIPC# component and the client-side widget relays them to
the other portlets, as illustrated in
<<figure.portal.liferay-ipc.architecture>>.
[[figure.portal.liferay-ipc.architecture]]
.Vaadin IPC for Liferay Architecture
image::img/liferay-ipc-architecture-hi.png[]
Vaadin IPC for Liferay uses the Liferay JavaScript event API for client-side
inter-portlet communication, so you can communicate just as easily with other
Liferay portlets.
Notice that you can use this communication only between portlets on the same
page.
<<figure.portal.liferay-ipc.demo>> shows Vaadin IPC for Liferay in action.
Entering a new item in one portlet is updated interactively in the other.
[[figure.portal.liferay-ipc.demo]]
.Vaadin IPC Add-on Demo with Two Portlets
image::img/liferay-ipc-demo-annotated-lo.png[]
[[portal.liferay-ipc.installation]]
== Installing the Add-on
The Vaadin IPC for Liferay add-on is available from the Vaadin Directory as well
as from a Maven repository. To download the installation package or find out the
Maven or Ivy dependency, see the
link:https://vaadin.com/directory#addon/vaadin-ipc-for-liferay[add-on page at
Vaadin Directory], and install the add-on as described in
<<dummy/../../../framework/addons/addons-overview.asciidoc#addons.overview,"Using
Vaadin Add-ons">>.
The contents of the installation package are as follows:
[filename]#vaadin-ipc-for-liferay-x.x.x.jar#:: The add-on JAR in the installation package must be installed in the [filename]#WEB-INF/lib# directory under the root context. The location depends on the server - for example in Liferay running in Tomcat it is located under the [filename]#webapps/ROOT# folder of the server.
[filename]#doc#:: The documentation folder includes a [filename]#README.TXT# file that describes the contents of the installation package briefly, and [filename]#licensing.txt# and [filename]#license-asl-2.0.txt#, which describe the licensing under the Apache License 2.0. Under the [filename]#doc/api# folder is included the complete JavaDoc API documentation for the add-on.
[filename]#vaadin-ipc-for-liferay-x.x.x-demo.war#:: A WAR containing demo portlets. After installing the add-on library and compiling the widget set, as described below, you can deploy the WAR to Liferay and add the two demo portlets to a page, as shown in <<figure.portal.liferay-ipc.demo>>. The source of the demo is available at link:http://dev.vaadin.com/svn/addons/IPCforLiferay/trunk/demo/src/com/vaadin/addon/ipcforliferay/demo/[dev.vaadin.com/svn/addons/IPCforLiferay/trunk/].
The add-on contains a widget set, which you must compile into the Vaadin widget
set installed in the portal.
[[portal.liferay-ipc.communication]]
== Basic Communication
[classname]#LiferayIPC# is an invisible user interface component that can be
used to send messages between two or more Vaadin portlets. You add it to an
application layout as you would any regular user interface component.
----
LiferayIPC liferayipc = new LiferayIPC();
layout.addComponent(liferayipc);
----
You should be careful not to remove the invisible component from the portlet
later if you modify the layout of the portlet.
The component can be used both for sending and receiving messages, as described
next.
[[portal.liferay-ipc.communication.sending]]
=== Sending Events
You can send an event (a message) with the [methodname]#sendEvent()# method,
which takes an event ID and the message data as parameters. The event is
broadcast to all listening portlets. The event ID is a string that can be used
to identify the recipient of an event or the event type.
----
liferayipc.sendEvent("hello", "This is Data");
----
If you need to send more complex data, you need to format or serialize it to a
string representation as described in <<portal.liferay-ipc.serialization>>.
[[portal.liferay-ipc.communication.receiving]]
=== Receiving Events
A portlet wishing to receive events (messages) from other portlets needs to
register a listener in the component with [methodname]#addListener()#. The
listener receives the messages in a [classname]#LiferayIPCEvent# object.
Filtering events by the ID is built in into the listener handler, you give the
listened event ID as the first parameter for the [methodname]#addListener()#.
The actual message data is held in the [parameter]#data# property, which you can
read with [methodname]#getData()#.
----
liferayipc.addListener("hello", new LiferayIPCEventListener() {
public void eventReceived(LiferayIPCEvent event) {
// Do something with the message data
String data = event.getData();
Notification.show("Received hello: " + data);
}
});
----
A listener added to a [classname]#LiferayIPC# can be removed with
[methodname]#removeListener()#.
[[portal.liferay-ipc.concerns]]
== Considerations
Both security and efficiency should be considered with inter-portlet
communications when using the Vaadin IPC for Liferay.
[[portal.liferay-ipc.concerns.security]]
=== Browser Security
As the message data is passed through the client-side (browser), any code
running in the browser has access to the data. You should be careful not to
expose any security-critical data in client-side messaging. Also, malicious code
running in the browser could alter or fake messages. Sanitization can help with
the latter problem and encryption to solve the both issues. You can also share
the sensitive data through session attributes or a database and use the
client-side IPC only to notify that the data is available.
[[portal.liferay-ipc.concerns.efficiency]]
=== Efficiency
Sending data through the browser requires loading and sending it in HTTP
requests. The data is held in the memory space of the browser, and handling
large data in the client-side JavaScript code can take time. Noticeably large
message data can therefore reduce the responsiveness of the application and
could, in extreme cases, go over browser limits for memory consumption or
JavaScript execution time.
[[portal.liferay-ipc.attributes]]
== Communication Through Session Attributes
In many cases, such as when considering security or efficiency, it is better to
pass the bulk data on the server-side and use the client-side IPC only for
notifying the other portlet(s) that the data is available. Session attributes
are a conveninent way of sharing data on the server-side. You can also share
objects through them, not just strings.
The session variables have a __scope__, which should be
[parameter]#APPLICATION_SCOPE#. The "application" refers to the scope of the
Java web application (WAR) that contains the portlets.
If the communicating portlets are in the same Java web application (WAR), no
special configuration is needed. You can also communicate between portlets in
different WARs, in which case you need to disable the
[parameter]#private-session-attributes# parameter in
[filename]#liferay-portlet.xml# by setting it to [literal]#++false++#. Please
see Liferay documentation for more information regarding the configuration.
You can also share Java objects between the portlets in the same WAR, not just
strings. If the portlets are in different WARs, they normally have different
class loaders, which could cause incompatibilities, so you can only communicate
with strings and any object data needs to be serialized.
Session attributes are accessible through the [classname]#PortletSession#
object, which you can access through the portlet context from the Vaadin
[classname]#Application# class.
----
Person person = new Person(firstname, lastname, age);
...
PortletSession session =
((PortletApplicationContext2)getContext()).
getPortletSession();
// Share the object
String key = "IPCDEMO_person";
session.setAttribute(key, person,
PortletSession.APPLICATION_SCOPE);
// Notify that it's available
liferayipc.sendEvent("ipc_demodata_available", key);
----
You can then receive the attribute in a [classname]#LiferayIPCEventListener# as
follows:
----
public void eventReceived(LiferayIPCEvent event) {
String key = event.getData();
PortletSession session =
((PortletApplicationContext2)getContext()).
getPortletSession();
// Get the object reference
Person person = (Person) session.getAttribute(key);
// We can now use the object in our application
BeanItem<Person> item = new BeanItem<Person> (person);
form.setItemDataSource(item);
}
----
Notice that changes to a shared object bound to a user interface component are
not updated automatically if it is changed in another portlet. The issue is the
same as with double-binding in general.
[[portal.liferay-ipc.serialization]]
== Serializing and Encoding Data
The IPC events support transmitting only plain strings, so if you have object or
other non-string data, you need to format or serialize it to a string
representation. For example, the demo application formats the trivial data model
as a semicolon-separated list as follows:
----
private void sendPersonViaClient(String firstName,
String lastName, int age) {
liferayIPC_1.sendEvent("newPerson", firstName + ";" +
lastName + ";" + age);
}
----
You can use standard Java serialization for any classes that implement the
[interfacename]#Serializable# interface. The transmitted data may not include
any control characters, so you also need to encode the string, for example by
using Base64 encoding.
----
// Some serializable object
MyBean mybean = new MyBean();
...
// Serialize
ByteArrayOutputStream baostr = new ByteArrayOutputStream();
ObjectOutputStream oostr;
try {
oostr = new ObjectOutputStream(baostr);
oostr.writeObject(mybean); // Serialize the object
oostr.close();
} catch (IOException e) {
Notification.show("IO PAN!"); // Complain
}
// Encode
BASE64Encoder encoder = new BASE64Encoder();
String encoded = encoder.encode(baostr.toByteArray());
// Send the IPC event to other portlet(s)
liferayipc.sendEvent("mybeanforyou", encoded);
----
You can then deserialize such a message at the receiving end as follows:
----
public void eventReceived(LiferayIPCEvent event) {
String encoded = event.getData();
// Decode and deserialize it
BASE64Decoder decoder = new BASE64Decoder();
try {
byte[] data = decoder.decodeBuffer(encoded);
ObjectInputStream ois =
new ObjectInputStream(
new ByteArrayInputStream(data));
// The deserialized bean
MyBean deserialized = (MyBean) ois.readObject();
ois.close();
... do something with the bean ...
} catch (IOException e) {
e.printStackTrace(); // Handle somehow
} catch (ClassNotFoundException e) {
e.printStackTrace(); // Handle somehow
}
}
----
[[portal.liferay-ipc.nonvaadin]]
== Communicating with Non-Vaadin Portlets
You can use the Vaadin IPC for Liferay to communicate also between a Vaadin
application and other portlets, such as JSP portlets. The add-on passes the
events as regular Liferay JavaScript events. The demo WAR includes two JSP
portlets that demonstrate the communication.
When sending events from non-Vaadin portlet, fire the event using the JavaScript
[methodname]#Liferay.fire()# method with an event ID and message. For example,
in JSP you could have:
----
<%@ taglib uri="http://java.sun.com/portlet_2_0"
prefix="portlet" %>
<portlet:defineObjects />
<script>
function send_message() {
Liferay.fire('hello', "Hello, I'm here!");
}
</script>
<input type="button" value="Send message"
onclick="send_message()" />
----
You can receive events using a Liferay JavaScript event handler. You define the
handler with the [methodname]#on()# method in the Liferay object. It takes the
event ID and a callback function as its parameters. Again in JSP you could have:
----
<%@ taglib uri="http://java.sun.com/portlet_2_0"
prefix="portlet" %>
<portlet:defineObjects />
<script>
Liferay.on('hello', function(event, data) {
alert("Hello: " + data);
});
</script>
----
(((range="endofrange", startref="term.portal.liferay-ipc")))
|