This repository has been archived by the owner on Nov 10, 2017. It is now read-only.
forked from VaclavDedik/jboss-seam
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Remoting.xml
executable file
·806 lines (597 loc) · 39.8 KB
/
Remoting.xml
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
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<chapter id="remoting">
<title>Remoting</title>
<para> Seam provides a convenient method of remotely accessing components from a web page, using AJAX (Asynchronous
Javascript and XML). The framework for this functionality is provided with almost no up-front development effort -
your components only require simple annotating to become accessible via AJAX. This chapter describes the steps
required to build an AJAX-enabled web page, then goes on to explain the features of the Seam Remoting framework in
more detail. </para>
<section>
<title>Configuration</title>
<para> To use remoting, the Seam Resource servlet must first be configured in your <literal>web.xml</literal> file: </para>
<programlisting role="XML"><![CDATA[<servlet>
<servlet-name>Seam Resource Servlet</servlet-name>
<servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Seam Resource Servlet</servlet-name>
<url-pattern>/seam/resource/*</url-pattern>
</servlet-mapping>]]></programlisting>
<para> The next step is to import the necessary Javascript into your web page. There are a minimum of two scripts
that must be imported. The first one contains all the client-side framework code that enables remoting
functionality: </para>
<programlisting role="XHTML"><![CDATA[<script type="text/javascript" src="seam/resource/remoting/resource/remote.js"></script>]]></programlisting>
<para> The second script contains the stubs and type definitions for the components you wish to call. It is
generated dynamically based on the local interface of your components, and includes type definitions for all of
the classes that can be used to call the remotable methods of the interface. The name of the script reflects the
name of your component. For example, if you have a stateless session bean annotated with
<literal>@Name("customerAction")</literal>, then your script tag should look like this: </para>
<programlisting role="XHTML"><![CDATA[<script type="text/javascript"
src="seam/resource/remoting/interface.js?customerAction"></script>]]></programlisting>
<para> If you wish to access more than one component from the same page, then include them all as parameters of your
script tag: </para>
<programlisting role="XHTML"><![CDATA[<script type="text/javascript"
src="seam/resource/remoting/interface.js?customerAction&accountAction"></script>]]></programlisting>
<para>
Alternatively, you may use the <literal>s:remote</literal> tag to import the required Javascript. Separate each
component or class name you wish to import with a comma:
</para>
<programlisting role="XHTML"><![CDATA[
<s:remote include="customerAction,accountAction"/>
]]></programlisting>
</section>
<section>
<title>The "Seam" object</title>
<para> Client-side interaction with your components is all performed via the <literal>Seam</literal> Javascript
object. This object is defined in <literal>remote.js</literal>, and you'll be using it to make asynchronous calls
against your component. It is split into two areas of functionality; <literal>Seam.Component</literal> contains
methods for working with components and <literal>Seam.Remoting</literal> contains methods for executing remote
requests. The easiest way to become familiar with this object is to start with a simple example. </para>
<section>
<title>A Hello World example</title>
<para> Let's step through a simple example to see how the <literal>Seam</literal> object works. First of all,
let's create a new Seam component called <literal>helloAction</literal>. </para>
<programlisting role="JAVA"><![CDATA[@Stateless
@Name("helloAction")
public class HelloAction {
@WebRemote
public String sayHello(String name) {
return "Hello, " + name;
}
}]]></programlisting>
<para>Take special note of the
<literal>@WebRemote</literal> annotation, as it's required to make our method accessible via remoting:</para>
<para> That's all the server-side code we need to write.</para>
<note>
<para>If you are performing a persistence operation in the method marked <literal>@WebRemote</literal> you will
also need to add a <literal>@Transactional</literal> annotation to the method. Otherwise, your method would
execute outside of a transaction without this extra hint.That's because unlike a JSF request, Seam does not
wrap the remoting request in a transaction automatically.</para>
</note>
<para>Now for our web page - create a new page and import the
<literal>helloAction</literal> component: </para>
<programlisting role="XHTML"><![CDATA[<s:remote include="helloAction"/>]]></programlisting>
<para> To make this a fully interactive user experience, let's add a button to our page: </para>
<programlisting role="XHTML"><![CDATA[<button onclick="javascript:sayHello()">Say Hello</button>]]></programlisting>
<para> We'll also need to add some more script to make our button actually do something when it's clicked: </para>
<programlisting role="XHTML"><![CDATA[<script type="text/javascript">
//<![CDATA[
function sayHello() {
var name = prompt("What is your name?");
Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);
}
function sayHelloCallback(result) {
alert(result);
}
// ]]>]]><![CDATA[
</script>]]></programlisting>
<para> We're done! Deploy your application and browse to your page. Click the button, and enter a name when
prompted. A message box will display the hello message confirming that the call was successful. If you want to
save some time, you'll find the full source code for this Hello World example in Seam's
<literal>/examples/remoting/helloworld</literal> directory. </para>
<para> So what does the code of our script actually do? Let's break it down into smaller pieces. To start with,
you can see from the Javascript code listing that we have implemented two methods - the first method is
responsible for prompting the user for their name and then making a remote request. Take a look at the following
line: </para>
<programlisting role="XHTML">Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);</programlisting>
<para> The first section of this line, <literal>Seam.Component.getInstance("helloAction")</literal> returns a
proxy, or "stub" for our <literal>helloAction</literal> component. We can invoke the methods of our component
against this stub, which is exactly what happens with the remainder of the line: <literal>sayHello(name,
sayHelloCallback);</literal>. </para>
<para> What this line of code in its completeness does, is invoke the <literal>sayHello</literal> method of our
component, passing in <literal>name</literal> as a parameter. The second parameter,
<literal>sayHelloCallback</literal> isn't a parameter of our component's <literal>sayHello</literal> method,
instead it tells the Seam Remoting framework that once it receives the response to our request, it should pass
it to the <literal>sayHelloCallback</literal> Javascript method. This callback parameter is entirely optional,
so feel free to leave it out if you're calling a method with a <literal>void</literal> return type or if you
don't care about the result. </para>
<para> The <literal>sayHelloCallback</literal> method, once receiving the response to our remote request then pops
up an alert message displaying the result of our method call. </para>
</section>
<section>
<title>Seam.Component</title>
<para> The <literal>Seam.Component</literal> Javascript object provides a number of client-side methods for
working with your Seam components. The two main methods, <literal>newInstance()</literal> and
<literal>getInstance()</literal> are documented in the following sections however their main difference is
that <literal>newInstance()</literal> will always create a new instance of a component type, and
<literal>getInstance()</literal> will return a singleton instance. </para>
<section>
<title>Seam.Component.newInstance()</title>
<para> Use this method to create a new instance of an entity or Javabean component. The object returned by this
method will have the same getter/setter methods as its server-side counterpart, or alternatively if you wish
you can access its fields directly. Take the following Seam entity component for example: </para>
<programlisting role="JAVA">@Name("customer")
@Entity
public class Customer implements Serializable
{
private Integer customerId;
private String firstName;
private String lastName;
@Column public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId} {
this.customerId = customerId;
}
@Column public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Column public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}</programlisting>
<para> To create a client-side Customer you would write the following code: </para>
<programlisting role="XHTML">var customer = Seam.Component.newInstance("customer");</programlisting>
<para> Then from here you can set the fields of the customer object: </para>
<programlisting role="XHTML">customer.setFirstName("John");
// Or you can set the fields directly
customer.lastName = "Smith";</programlisting>
</section>
<section>
<title>Seam.Component.getInstance()</title>
<para> The <literal>getInstance()</literal> method is used to get a reference to a Seam session bean component
stub, which can then be used to remotely execute methods against your component. This method returns a
singleton for the specified component, so calling it twice in a row with the same component name will return
the same instance of the component. </para>
<para> To continue our example from before, if we have created a new <literal>customer</literal> and we now wish
to save it, we would pass it to the <literal>saveCustomer()</literal> method of our
<literal>customerAction</literal> component: </para>
<programlisting role="XHTML">Seam.Component.getInstance("customerAction").saveCustomer(customer);</programlisting>
</section>
<section>
<title>Seam.Component.getComponentName()</title>
<para> Passing an object into this method will return its component name if it is a component, or
<literal>null</literal> if it is not. </para>
<programlisting role="XHTML">if (Seam.Component.getComponentName(instance) == "customer")
alert("Customer");
else if (Seam.Component.getComponentName(instance) == "staff")
alert("Staff member");</programlisting>
</section>
</section>
<section>
<title>Seam.Remoting</title>
<para> Most of the client side functionality for Seam Remoting is contained within the
<literal>Seam.Remoting</literal> object. While you shouldn't need to directly call most of its methods, there
are a couple of important ones worth mentioning. </para>
<section>
<title>Seam.Remoting.createType()</title>
<para> If your application contains or uses Javabean classes that aren't Seam components, you may need to create
these types on the client side to pass as parameters into your component method. Use the
<literal>createType()</literal> method to create an instance of your type. Pass in the fully qualified Java
class name as a parameter: </para>
<programlisting role="XHTML">var widget = Seam.Remoting.createType("com.acme.widgets.MyWidget");</programlisting>
</section>
<section>
<title>Seam.Remoting.getTypeName()</title>
<para> This method is the equivalent of <literal>Seam.Component.getComponentName()</literal> but for
non-component types. It will return the name of the type for an object instance, or <literal>null</literal> if
the type is not known. The name is the fully qualified name of the type's Java class. </para>
</section>
</section>
</section>
<!--section>
<title>Evaluating EL Expressions</title>
<para>
Seam Remoting also supports the evaluation of EL expressions, which provides another convenient method for retrieving
data from the server. Using the <literal>Seam.Remoting.eval()</literal> function, an EL expression can be remotely
evaluated on the server and the resulting value returned to a client-side callback method. This function accepts two
parameters, the first being the EL expression to evaluate, and the second being the callback method to invoke with the
value of the expression. Here's an example:
</para>
<programlisting role="XHTML"><![CDATA[ function customersCallback(customers) {
for (var i = 0; i < customers.length; i++) {
alert("Got customer: " + customers[i].getName());
}
}
Seam.Remoting.eval("#{customers}", customersCallback);
]]></programlisting>
<para>
In this example, the expression <literal>#{customers}</literal> is evaluated by Seam, and the value of the expression
(in this case a list of Customer objects) is returned to the <literal>customersCallback()</literal> method. It is
important to remember that the objects returned this way must have their types imported (via <literal>s:remote</literal>)
to be able to work with them in Javascript. So to work with a list of <literal>customer</literal> objects,
it is required to import the <literal>customer</literal> type:
</para>
<programlisting role="XHTML"><![CDATA[<s:remote include="customer"/>]]></programlisting>
</section-->
<section>
<title>Client Interfaces</title>
<para> In the configuration section above, the interface, or "stub" for our component is imported into our page
either via <literal>seam/resource/remoting/interface.js</literal>: or using the <literal>s:remote</literal>
tag:
</para>
<programlisting role="XHTML"><![CDATA[<script type="text/javascript"
src="seam/resource/remoting/interface.js?customerAction"></script>]]></programlisting>
<programlisting role="XHTML"><![CDATA[<s:remote include="customerAction"/>]]></programlisting>
<para> By including this script in our page, the interface definitions for our component, plus any other components
or types that are required to execute the methods of our component are generated and made available for the
remoting framework to use. </para>
<para> There are two types of client stub that can be generated, "executable" stubs and "type" stubs. Executable
stubs are behavioural, and are used to execute methods against your session bean components, while type stubs
contain state and represent the types that can be passed in as parameters or returned as a result. </para>
<para> The type of client stub that is generated depends on the type of your Seam component. If the component is a
session bean, then an executable stub will be generated, otherwise if it's an entity or JavaBean, then a type stub
will be generated. There is one exception to this rule; if your component is a JavaBean (ie it is not a session
bean nor an entity bean) and any of its methods are annotated with @WebRemote, then an executable stub will be
generated for it instead of a type stub. This allows you to use remoting to call methods of your JavaBean
components in a non-EJB environment where you don't have access to session beans. </para>
</section>
<section>
<title>The Context</title>
<para> The Seam Remoting Context contains additional information which is sent and received as part of a remoting
request/response cycle. At this stage it only contains the conversation ID but may be expanded in the future. </para>
<section>
<title>Setting and reading the Conversation ID</title>
<para> If you intend on using remote calls within the scope of a conversation then you need to be able to read or
set the conversation ID in the Seam Remoting Context. To read the conversation ID after making a remote request
call <literal>Seam.Remoting.getContext().getConversationId()</literal>. To set the conversation ID before making a
request, call <literal>Seam.Remoting.getContext().setConversationId()</literal>. </para>
<para> If the conversation ID hasn't been explicitly set with
<literal>Seam.Remoting.getContext().setConversationId()</literal>, then it will be automatically assigned the
first valid conversation ID that is returned by any remoting call. If you are working with multiple conversations
within your page, then you may need to explicitly set the conversation ID before each call. If you are working
with just a single conversation, then you don't need to do anything special. </para>
</section>
<section>
<title>Remote calls within the current conversation scope</title>
<para> In some circumstances it may be required to make a remote call within the scope of the
current view's conversation. To do this, you must explicitly set the conversation ID to that
of the view before making the remote call. This small snippet of JavaScript will set the
conversation ID that is used for remoting calls to the current view's conversation ID: </para>
<programlisting role="XHTML"><![CDATA[Seam.Remoting.getContext().setConversationId( #{conversation.id} );]]></programlisting>
</section>
</section>
<section>
<title>Batch Requests</title>
<para> Seam Remoting allows multiple component calls to be executed within a single request. It is recommended that
this feature is used wherever it is appropriate to reduce network traffic. </para>
<para> The method <literal>Seam.Remoting.startBatch()</literal> will start a new batch, and any component calls
executed after starting a batch are queued, rather than being sent immediately. When all the desired component
calls have been added to the batch, the <literal>Seam.Remoting.executeBatch()</literal> method will send a single
request containing all of the queued calls to the server, where they will be executed in order. After the calls
have been executed, a single response containning all return values will be returned to the client and the
callback functions (if provided) triggered in the same order as execution. </para>
<para> If you start a new batch via the <literal>startBatch()</literal> method but then decide you don't want to
send it, the <literal>Seam.Remoting.cancelBatch()</literal> method will discard any calls that were queued and
exit the batch mode. </para>
<para> To see an example of a batch being used, take a look at <literal>/examples/remoting/chatroom</literal>.
</para>
</section>
<section>
<title>Working with Data types</title>
<section>
<title>Primitives / Basic Types</title>
<para> This section describes the support for basic data types. On the server side these values are generally
compatible with either their primitive type or their corresponding wrapper class. </para>
<section>
<title>String</title>
<para> Simply use Javascript String objects when setting String parameter values. </para>
</section>
<section>
<title>Number</title>
<para> There is support for all number types supported by Java. On the client side, number values are always
serialized as their String representation and then on the server side they are converted to the correct
destination type. Conversion into either a primitive or wrapper type is supported for <literal>Byte</literal>,
<literal>Double</literal>, <literal>Float</literal>, <literal>Integer</literal>, <literal>Long</literal> and
<literal>Short</literal> types. </para>
</section>
<section>
<title>Boolean</title>
<para> Booleans are represented client side by Javascript Boolean values, and server side by a Java boolean.
</para>
</section>
</section>
<section>
<title>JavaBeans</title>
<para> In general these will be either Seam entity or JavaBean components, or some other non-component class. Use
the appropriate method (either <literal>Seam.Component.newInstance()</literal> for Seam components or
<literal>Seam.Remoting.createType()</literal> for everything else) to create a new instance of the object. </para>
<para> It is important to note that only objects that are created by either of these two methods should be used as
parameter values, where the parameter is not one of the other valid types mentioned anywhere else in this
section. In some situations you may have a component method where the exact parameter type cannot be determined,
such as: </para>
<programlisting role="JAVA">@Name("myAction")
public class MyAction implements MyActionLocal {
public void doSomethingWithObject(Object obj) {
// code
}
}</programlisting>
<para> In this case you might want to pass in an instance of your <literal>myWidget</literal> component, however
the interface for <literal>myAction</literal> won't include <literal>myWidget</literal> as it is not directly
referenced by any of its methods. To get around this, <literal>MyWidget</literal> needs to be explicitly
imported: </para>
<programlisting role="XHTML"><![CDATA[<s:remote include="myAction,myWidget"/>]]></programlisting>
<para> This will then allow a <literal>myWidget</literal> object to be created with
<literal>Seam.Component.newInstance("myWidget")</literal>, which can then be passed to
<literal>myAction.doSomethingWithObject()</literal>. </para>
</section>
<section>
<title>Dates and Times</title>
<para> Date values are serialized into a String representation that is accurate to the millisecond. On the client
side, use a Javascript Date object to work with date values. On the server side, use any
<literal>java.util.Date</literal> (or descendent, such as <literal>java.sql.Date</literal> or
<literal>java.sql.Timestamp</literal> class. </para>
</section>
<section>
<title>Enums</title>
<para> On the client side, enums are treated the same as Strings. When setting the value for an enum parameter,
simply use the String representation of the enum. Take the following component as an example: </para>
<programlisting role="JAVA">@Name("paintAction")
public class paintAction implements paintLocal {
public enum Color {red, green, blue, yellow, orange, purple};
public void paint(Color color) {
// code
}
} </programlisting>
<para> To call the <literal>paint()</literal> method with the color <literal>red</literal>, pass the parameter
value as a String literal: </para>
<programlisting role="XHTML">Seam.Component.getInstance("paintAction").paint("red");</programlisting>
<para> The inverse is also true - that is, if a component method returns an enum parameter (or contains an enum
field anywhere in the returned object graph) then on the client-side it will be represented as a String. </para>
</section>
<section>
<title>Collections</title>
<section>
<title>Bags</title>
<para> Bags cover all collection types including arrays, collections, lists, sets, (but excluding Maps - see the
next section for those), and are implemented client-side as a Javascript array. When calling a component
method that accepts one of these types as a parameter, your parameter should be a Javascript array. If a
component method returns one of these types, then the return value will also be a Javascript array. The
remoting framework is clever enough on the server side to convert the bag to an appropriate type for the
component method call. </para>
</section>
<section>
<title>Maps</title>
<para> As there is no native support for Maps within Javascript, a simple Map implementation is provided with
the Seam Remoting framework. To create a Map which can be used as a parameter to a remote call, create a new
<literal>Seam.Remoting.Map</literal> object: </para>
<programlisting role="XHTML">var map = new Seam.Remoting.Map();</programlisting>
<para> This Javascript implementation provides basic methods for working with Maps: <literal>size()</literal>,
<literal>isEmpty()</literal>, <literal>keySet()</literal>, <literal>values()</literal>,
<literal>get(key)</literal>, <literal>put(key, value)</literal>, <literal>remove(key)</literal> and
<literal>contains(key)</literal>. Each of these methods are equivalent to their Java counterpart. Where the
method returns a collection, such as <literal>keySet()</literal> and <literal>values()</literal>, a Javascript
Array object will be returned that contains the key or value objects (respectively). </para>
</section>
</section>
</section>
<section>
<title>Debugging</title>
<para> To aid in tracking down bugs, it is possible to enable a debug mode which will display the contents of all
the packets send back and forth between the client and server in a popup window. To enable debug mode, either
execute the <literal>setDebug()</literal> method in Javascript: </para>
<programlisting role="XHTML">Seam.Remoting.setDebug(true);</programlisting>
<para> Or configure it via components.xml: </para>
<programlisting role="XML"><![CDATA[<remoting:remoting debug="true"/>]]></programlisting>
<para> To turn off debugging, call <literal>setDebug(false)</literal>. If you want to write your own messages to the
debug log, call <literal>Seam.Remoting.log(message)</literal>. </para>
</section>
<section>
<title>Handling Exceptions</title>
<para>
When invoking a remote component method, it is possible to specify an exception handler which will process
the response in the event of an exception during component invocation. To specify an exception handler function,
include a reference to it after the callback parameter in your JavaScript:
</para>
<programlisting><![CDATA[var callback = function(result) { alert(result); };
var exceptionHandler = function(ex) { alert("An exception occurred: " + ex.getMessage()); };
Seam.Component.getInstance("helloAction").sayHello(name, callback, exceptionHandler);]]></programlisting>
<para>
If you do not have a callback handler defined, you must specify <literal>null</literal> in its place:
</para>
<programlisting><![CDATA[var exceptionHandler = function(ex) { alert("An exception occurred: " + ex.getMessage()); };
Seam.Component.getInstance("helloAction").sayHello(name, null, exceptionHandler);]]></programlisting>
<para>
The exception object that is passed to the exception handler exposes one method, <literal>getMessage()</literal>
that returns the exception message which is produced by the exception thrown by the <literal>@WebRemote</literal>
method.
</para>
</section>
<section>
<title>The Loading Message</title>
<para> The default loading message that appears in the top right corner of the screen can be modified, its rendering
customised or even turned off completely. </para>
<section>
<title>Changing the message</title>
<para> To change the message from the default "Please Wait..." to something different, set the value of
<literal>Seam.Remoting.loadingMessage</literal>: </para>
<programlisting role="XHTML">Seam.Remoting.loadingMessage = "Loading..."; </programlisting>
</section>
<section>
<title>Hiding the loading message</title>
<para> To completely suppress the display of the loading message, override the implementation of
<literal>displayLoadingMessage()</literal> and <literal>hideLoadingMessage()</literal> with functions that
instead do nothing: </para>
<programlisting role="XHTML">// don't display the loading indicator
Seam.Remoting.displayLoadingMessage = function() {};
Seam.Remoting.hideLoadingMessage = function() {};</programlisting>
</section>
<section>
<title>A Custom Loading Indicator</title>
<para> It is also possible to override the loading indicator to display an animated icon, or anything else that
you want. To do this override the <literal>displayLoadingMessage()</literal> and
<literal>hideLoadingMessage()</literal> messages with your own implementation: </para>
<programlisting role="XHTML"> Seam.Remoting.displayLoadingMessage = function() {
// Write code here to display the indicator
};
Seam.Remoting.hideLoadingMessage = function() {
// Write code here to hide the indicator
};</programlisting>
</section>
</section>
<section>
<title>Controlling what data is returned</title>
<para> When a remote method is executed, the result is serialized into an XML response that is returned to the
client. This response is then unmarshaled by the client into a Javascript object. For complex types (i.e.
Javabeans) that include references to other objects, all of these referenced objects are also serialized as part
of the response. These objects may reference other objects, which may reference other objects, and so forth. If
left unchecked, this object "graph" could potentially be enormous, depending on what relationships exist between
your objects. And as a side issue (besides the potential verbosity of the response), you might also wish to
prevent sensitive information from being exposed to the client. </para>
<para> Seam Remoting provides a simple means to "constrain" the object graph, by specifying the
<literal>exclude</literal> field of the remote method's <literal>@WebRemote</literal> annotation. This field
accepts a String array containing one or more paths specified using dot notation. When invoking a remote method,
the objects in the result's object graph that match these paths are excluded from the serialized result packet. </para>
<para> For all our examples, we'll use the following <literal>Widget</literal> class: </para>
<programlisting role="JAVA">@Name("widget")
public class Widget
{
private String value;
private String secret;
private Widget child;
private Map<String,Widget> widgetMap;
private List<Widget> widgetList;
// getters and setters for all fields
}</programlisting>
<section>
<title>Constraining normal fields</title>
<para> If your remote method returns an instance of <literal>Widget</literal>, but you don't want to expose the
<literal>secret</literal> field because it contains sensitive information, you would constrain it like this: </para>
<programlisting role="JAVA">@WebRemote(exclude = {"secret"})
public Widget getWidget(); </programlisting>
<para> The value "secret" refers to the <literal>secret</literal> field of the returned object. Now, suppose that
we don't care about exposing this particular field to the client. Instead, notice that the
<literal>Widget</literal> value that is returned has a field <literal>child</literal> that is also a
<literal>Widget</literal>. What if we want to hide the <literal>child</literal>'s <literal>secret</literal>
value instead? We can do this by using dot notation to specify this field's path within the result's object
graph: </para>
<programlisting role="JAVA">@WebRemote(exclude = {"child.secret"})
public Widget getWidget();</programlisting>
</section>
<section>
<title>Constraining Maps and Collections</title>
<para> The other place that objects can exist within an object graph are within a <literal>Map</literal> or some
kind of collection (<literal>List</literal>, <literal>Set</literal>, <literal>Array</literal>, etc). Collections
are easy, and are treated like any other field. For example, if our <literal>Widget</literal> contained a list
of other <literal>Widget</literal>s in its <literal>widgetList</literal> field, to constrain the
<literal>secret</literal> field of the <literal>Widget</literal>s in this list the annotation would look like
this: </para>
<programlisting role="JAVA">@WebRemote(exclude = {"widgetList.secret"})
public Widget getWidget();</programlisting>
<para> To constrain a <literal>Map</literal>'s key or value, the notation is slightly different. Appending
<literal>[key]</literal> after the <literal>Map</literal>'s field name will constrain the
<literal>Map</literal>'s key object values, while <literal>[value]</literal> will constrain the value object
values. The following example demonstrates how the values of the <literal>widgetMap</literal> field have their
<literal>secret</literal> field constrained: </para>
<programlisting role="JAVA">@WebRemote(exclude = {"widgetMap[value].secret"})
public Widget getWidget(); </programlisting>
</section>
<section>
<title>Constraining objects of a specific type</title>
<para> There is one last notation that can be used to constrain the fields of a type of object no matter where in
the result's object graph it appears. This notation uses either the name of the component (if the object is a
Seam component) or the fully qualified class name (only if the object is not a Seam component) and is expressed
using square brackets: </para>
<programlisting role="JAVA">@WebRemote(exclude = {"[widget].secret"})
public Widget getWidget(); </programlisting>
</section>
<section>
<title>Combining Constraints</title>
<para> Constraints can also be combined, to filter objects from multiple paths within the object graph: </para>
<programlisting role="JAVA">@WebRemote(exclude = {"widgetList.secret", "widgetMap[value].secret"})
public Widget getWidget();</programlisting>
</section>
</section>
<section>
<title>Transactional Requests</title>
<para>
By default there is no active transaction during a remoting request, so if you wish to perform database updates
during a remoting request, you need to annotate the <literal>@WebRemote</literal> method with
<literal>@Transactional</literal>, like so:
</para>
<programlisting><![CDATA[ @WebRemote @Transactional(TransactionPropagationType.REQUIRED)
public void updateOrder(Order order) {
entityManager.merge(order);
}]]></programlisting>
</section>
<section>
<title>JMS Messaging</title>
<para> Seam Remoting provides experimental support for JMS Messaging. This section describes the JMS support that is
currently implemented, but please note that this may change in the future. It is currently not recommended that
this feature is used within a production environment. </para>
<section>
<title>Configuration</title>
<para> Before you can subscribe to a JMS topic, you must first configure a list of the topics that can be
subscribed to by Seam Remoting. List the topics under
<literal>org.jboss.seam.remoting.messaging.subscriptionRegistry.allowedTopics</literal> in
<literal>seam.properties</literal>, <literal>web.xml</literal> or <literal>components.xml</literal>. </para>
<programlisting role="XML"><![CDATA[<remoting:remoting poll-timeout="5" poll-interval="1"/>]]></programlisting>
</section>
<section>
<title>Subscribing to a JMS Topic</title>
<para> The following example demonstrates how to subscribe to a JMS Topic: </para>
<programlisting role="XHTML">function subscriptionCallback(message)
{
if (message instanceof Seam.Remoting.TextMessage)
alert("Received message: " + message.getText());
}
Seam.Remoting.subscribe("topicName", subscriptionCallback);</programlisting>
<para> The <literal>Seam.Remoting.subscribe()</literal> method accepts two parameters, the first being the name of
the JMS Topic to subscribe to, the second being the callback function to invoke when a message is received. </para>
<para> There are two types of messages supported, Text messages and Object messages. If you need to test for the
type of message that is passed to your callback function you can use the <literal>instanceof</literal> operator
to test whether the message is a <literal>Seam.Remoting.TextMessage</literal> or
<literal>Seam.Remoting.ObjectMessage</literal>. A <literal>TextMessage</literal> contains the text value in
its <literal>text</literal> field (or alternatively call <literal>getText()</literal> on it), while an
<literal>ObjectMessage</literal> contains its object value in its <literal>value</literal> field (or call its
<literal>getValue()</literal> method). </para>
</section>
<section>
<title>Unsubscribing from a Topic</title>
<para> To unsubscribe from a topic, call <literal>Seam.Remoting.unsubscribe()</literal> and pass in the topic
name: </para>
<programlisting role="XHTML">Seam.Remoting.unsubscribe("topicName");</programlisting>
</section>
<section>
<title>Tuning the Polling Process</title>
<para> There are two parameters which you can modify to control how polling occurs. The first one is
<literal>Seam.Remoting.pollInterval</literal>, which controls how long to wait between subsequent polls for
new messages. This parameter is expressed in seconds, and its default setting is 10. </para>
<para> The second parameter is <literal>Seam.Remoting.pollTimeout</literal>, and is also expressed as seconds. It
controls how long a request to the server should wait for a new message before timing out and sending an empty
response. Its default is 0 seconds, which means that when the server is polled, if there are no messages ready
for delivery then an empty response will be immediately returned. </para>
<para> Caution should be used when setting a high <literal>pollTimeout</literal> value; each request that has to
wait for a message means that a server thread is tied up until a message is received, or until the request times
out. If many such requests are being served simultaneously, it could mean a large number of threads become tied
up because of this reason. </para>
<para> It is recommended that you set these options via components.xml, however they can be overridden via
Javascript if desired. The following example demonstrates how to configure the polling to occur much more
aggressively. You should set these parameters to suitable values for your application: </para>
<para> Via components.xml: </para>
<programlisting role="XML"><![CDATA[<remoting:remoting poll-timeout="5" poll-interval="1"/>]]></programlisting>
<para> Via JavaScript: </para>
<programlisting role="XHTML">// Only wait 1 second between receiving a poll response and sending the next poll request.
Seam.Remoting.pollInterval = 1;
// Wait up to 5 seconds on the server for new messages
Seam.Remoting.pollTimeout = 5; </programlisting>
</section>
</section>
</chapter>