forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathremoting.xml
More file actions
1648 lines (1324 loc) · 75.2 KB
/
remoting.xml
File metadata and controls
1648 lines (1324 loc) · 75.2 KB
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
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="remoting">
<title>Remoting and web services using Spring</title>
<section id="remoting-introduction">
<title>Introduction</title>
<para>Spring features integration classes for remoting support using
various technologies. The remoting support eases the development of
remote-enabled services, implemented by your usual (Spring) POJOs.
Currently, Spring supports the following remoting technologies: <itemizedlist>
<listitem>
<para><emphasis>Remote Method Invocation (RMI)</emphasis>. Through
the use of the <classname>RmiProxyFactoryBean</classname> and the
<classname>RmiServiceExporter</classname> Spring supports both
traditional RMI (with <interfacename>java.rmi.Remote</interfacename>
interfaces and
<exceptionname>java.rmi.RemoteException</exceptionname>) and
transparent remoting via RMI invokers (with any Java
interface).</para>
</listitem>
<listitem>
<para><emphasis>Spring's HTTP invoker</emphasis>. Spring provides a
special remoting strategy which allows for Java serialization via
HTTP, supporting any Java interface (just like the RMI invoker). The
corresponding support classes are
<classname>HttpInvokerProxyFactoryBean</classname> and
<classname>HttpInvokerServiceExporter</classname>.</para>
</listitem>
<listitem>
<para><emphasis>Hessian</emphasis>. By using Spring's
<classname>HessianProxyFactoryBean</classname> and the
<classname>HessianServiceExporter</classname> you can transparently
expose your services using the lightweight binary HTTP-based
protocol provided by Caucho.</para>
</listitem>
<listitem>
<para><emphasis>Burlap</emphasis>. Burlap is Caucho's XML-based
alternative to Hessian. Spring provides support classes such as
<classname>BurlapProxyFactoryBean</classname> and
<classname>BurlapServiceExporter</classname>.</para>
</listitem>
<listitem>
<para><emphasis>JAX-RPC</emphasis>. Spring provides remoting support
for web services via JAX-RPC (J2EE 1.4's web service API).</para>
</listitem>
<listitem>
<para><emphasis>JAX-WS</emphasis>. Spring provides remoting support
for web services via JAX-WS (the successor of JAX-RPC, as introduced
in Java EE 5 and Java 6).</para>
</listitem>
<listitem>
<para><emphasis>JMS</emphasis>. Remoting using JMS as the underlying
protocol is supported via the
<classname>JmsInvokerServiceExporter</classname> and
<classname>JmsInvokerProxyFactoryBean</classname> classes.</para>
</listitem>
</itemizedlist></para>
<para>While discussing the remoting capabilities of Spring, we'll use the
following domain model and corresponding services:</para>
<programlisting language="java">public class Account implements Serializable{
private String name;
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
}</programlisting>
<programlisting language="java">public interface AccountService {
public void insertAccount(Account account);
public List<Account> getAccounts(String name);
}</programlisting>
<programlisting language="java">public interface RemoteAccountService extends Remote {
public void insertAccount(Account account) throws RemoteException;
public List<Account> getAccounts(String name) throws RemoteException;
}</programlisting>
<programlisting language="java"><lineannotation>// the implementation doing nothing at the moment</lineannotation>
public class AccountServiceImpl implements AccountService {
public void insertAccount(Account acc) {
<lineannotation>// do something...</lineannotation>
}
public List<Account> getAccounts(String name) {
<lineannotation>// do something...</lineannotation>
}
}</programlisting>
<para>We will start exposing the service to a remote client by using RMI
and talk a bit about the drawbacks of using RMI. We'll then continue to
show an example using Hessian as the protocol.</para>
</section>
<section id="remoting-rmi">
<title>Exposing services using RMI</title>
<para>Using Spring's support for RMI, you can transparently expose your
services through the RMI infrastructure. After having this set up, you
basically have a configuration similar to remote EJBs, except for the fact
that there is no standard support for security context propagation or
remote transaction propagation. Spring does provide hooks for such
additional invocation context when using the RMI invoker, so you can for
example plug in security frameworks or custom security credentials
here.</para>
<section id="remoting-rmi-server">
<title>Exporting the service using the
<classname>RmiServiceExporter</classname></title>
<para>Using the <classname>RmiServiceExporter</classname>, we can expose
the interface of our AccountService object as RMI object. The interface
can be accessed by using <classname>RmiProxyFactoryBean</classname>, or
via plain RMI in case of a traditional RMI service. The
<classname>RmiServiceExporter</classname> explicitly supports the
exposing of any non-RMI services via RMI invokers.</para>
<para>Of course, we first have to set up our service in the Spring
container:</para>
<programlisting language="xml"><bean id="accountService" class="example.AccountServiceImpl">
<lineannotation><!-- any additional properties, maybe a DAO? --></lineannotation>
</bean></programlisting>
<para>Next we'll have to expose our service using the
<classname>RmiServiceExporter</classname>:</para>
<programlisting language="xml"><bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<lineannotation><!-- does not necessarily have to be the same name as the bean to be exported --></lineannotation>
<property name="serviceName" value="AccountService"/>
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
<lineannotation><!-- defaults to <literal>1099</literal> --></lineannotation>
<property name="registryPort" value="1199"/>
</bean></programlisting>
<para>As you can see, we're overriding the port for the RMI registry.
Often, your application server also maintains an RMI registry and it is
wise to not interfere with that one. Furthermore, the service name is
used to bind the service under. So right now, the service will be bound
at <literal>'rmi://HOST:1199/AccountService'</literal>. We'll use the
URL later on to link in the service at the client side.</para>
<note>
<para>The <literal>servicePort</literal> property has been omitted (it
defaults to 0). This means that an anonymous port will be used to
communicate with the service.</para>
</note>
</section>
<section id="remoting-rmi-client">
<title>Linking in the service at the client</title>
<para>Our client is a simple object using the
<interfacename>AccountService</interfacename> to manage accounts:</para>
<programlisting language="java">public class SimpleObject {
private AccountService accountService;
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
// additional methods using the accountService
}</programlisting>
<para>To link in the service on the client, we'll create a separate
Spring container, containing the simple object and the service linking
configuration bits:</para>
<programlisting language="xml"><bean class="example.SimpleObject">
<property name="accountService" ref="accountService"/>
</bean>
<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://HOST:1199/AccountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean></programlisting>
<para>That's all we need to do to support the remote account service on
the client. Spring will transparently create an invoker and remotely
enable the account service through the
<classname>RmiServiceExporter</classname>. At the client we're linking
it in using the <classname>RmiProxyFactoryBean</classname>.</para>
</section>
</section>
<section id="remoting-caucho-protocols">
<title>Using Hessian or Burlap to remotely call services via HTTP</title>
<para>Hessian offers a binary HTTP-based remoting protocol. It is
developed by Caucho and more information about Hessian itself can be found
at <ulink url="http://www.caucho.com"></ulink>.</para>
<section id="remoting-caucho-protocols-hessian">
<title>Wiring up the <classname>DispatcherServlet</classname> for
Hessian and co.</title>
<para>Hessian communicates via HTTP and does so using a custom servlet.
Using Spring's <classname>DispatcherServlet</classname> principles, as
known from Spring Web MVC usage, you can easily wire up such a servlet
exposing your services. First we'll have to create a new servlet in your
application (this is an excerpt from
<filename>'web.xml'</filename>):</para>
<programlisting language="xml"><servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/remoting/*</url-pattern>
</servlet-mapping></programlisting>
<para>You're probably familiar with Spring's
<classname>DispatcherServlet</classname> principles and if so, you know
that now you'll have to create a Spring container configuration resource
named <filename>'remoting-servlet.xml'</filename> (after the name of
your servlet) in the <filename class="directory">'WEB-INF'</filename>
directory. The application context will be used in the next
section.</para>
<para>Alternatively, consider the use of Spring's simpler
<classname>HttpRequestHandlerServlet</classname>. This allows you to
embed the remote exporter definitions in your root application context
(by default in <filename>'WEB-INF/applicationContext.xml'</filename>),
with individual servlet definitions pointing to specific exporter beans.
Each servlet name needs to match the bean name of its target exporter in
this case.</para>
</section>
<section id="remoting-caucho-protocols-hessian-server">
<title>Exposing your beans by using the
<classname>HessianServiceExporter</classname></title>
<para>In the newly created application context called
<literal>remoting-servlet.xml</literal>, we'll create a
<classname>HessianServiceExporter</classname> exporting your
services:</para>
<programlisting language="xml"><bean id="accountService" class="example.AccountServiceImpl">
<lineannotation><!-- any additional properties, maybe a DAO? --></lineannotation>
</bean>
<bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean></programlisting>
<para>Now we're ready to link in the service at the client. No explicit
handler mapping is specified, mapping request URLs onto services, so
<classname>BeanNameUrlHandlerMapping</classname> will be used: Hence,
the service will be exported at the URL indicated through its bean name
within the containing <classname>DispatcherServlet</classname>'s mapping
(as defined above):
<literal>'http://HOST:8080/remoting/AccountService'</literal>.</para>
<para>Alternatively, create a
<classname>HessianServiceExporter</classname> in your root application
context (e.g. in
<filename>'WEB-INF/applicationContext.xml'</filename>):</para>
<programlisting language="xml"><bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean></programlisting>
<para>In the latter case, define a corresponding servlet for this
exporter in <filename>'web.xml'</filename>, with the same end result:
The exporter getting mapped to the request path
<literal>/remoting/AccountService</literal>. Note that the servlet name
needs to match the bean name of the target exporter.</para>
<programlisting language="xml"><servlet>
<servlet-name>accountExporter</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>accountExporter</servlet-name>
<url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping></programlisting>
</section>
<section id="remoting-caucho-protocols-hessian-client">
<title>Linking in the service on the client</title>
<para>Using the <classname>HessianProxyFactoryBean</classname> we can
link in the service at the client. The same principles apply as with the
RMI example. We'll create a separate bean factory or application context
and mention the following beans where the
<classname>SimpleObject</classname> is using the
<interfacename>AccountService</interfacename> to manage accounts:</para>
<programlisting language="xml"><bean class="example.SimpleObject">
<property name="accountService" ref="accountService"/>
</bean>
<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean></programlisting>
</section>
<section id="remoting-caucho-protocols-burlap">
<title>Using Burlap</title>
<para>We won't discuss Burlap, the XML-based equivalent of Hessian, in
detail here, since it is configured and set up in exactly the same way
as the Hessian variant explained above. Just replace the word
<literal>Hessian</literal> with <literal>Burlap</literal> and you're all
set to go.</para>
</section>
<section id="remoting-caucho-protocols-security">
<title>Applying HTTP basic authentication to a service exposed through
Hessian or Burlap</title>
<para>One of the advantages of Hessian and Burlap is that we can easily
apply HTTP basic authentication, because both protocols are HTTP-based.
Your normal HTTP server security mechanism can easily be applied through
using the <literal>web.xml</literal> security features, for example.
Usually, you don't use per-user security credentials here, but rather
shared credentials defined at the
<literal>Hessian/BurlapProxyFactoryBean</literal> level (similar to a
JDBC <interfacename>DataSource</interfacename>).</para>
<programlisting language="xml"><bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors" ref="authorizationInterceptor"/>
</bean>
<bean id="authorizationInterceptor"
class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor">
<property name="authorizedRoles" value="administrator,operator"/>
</bean></programlisting>
<para>This is an example where we explicitly mention the
<classname>BeanNameUrlHandlerMapping</classname> and set an interceptor
allowing only administrators and operators to call the beans mentioned
in this application context.</para>
<note>
<para>Of course, this example doesn't show a flexible kind of security
infrastructure. For more options as far as security is concerned, have
a look at the Spring Security project at <ulink
url="http://static.springsource.org/spring-security/site/"></ulink>.</para>
</note>
</section>
</section>
<section id="remoting-httpinvoker">
<title>Exposing services using HTTP invokers</title>
<para>As opposed to Burlap and Hessian, which are both lightweight
protocols using their own slim serialization mechanisms, Spring HTTP
invokers use the standard Java serialization mechanism to expose services
through HTTP. This has a huge advantage if your arguments and return types
are complex types that cannot be serialized using the serialization
mechanisms Hessian and Burlap use (refer to the next section for more
considerations when choosing a remoting technology).</para>
<para>Under the hood, Spring uses either the standard facilities provided
by J2SE to perform HTTP calls or Commons
<classname>HttpClient</classname>. Use the latter if you need more
advanced and easy-to-use functionality. Refer to <ulink
url="http://jakarta.apache.org/commons/httpclient">jakarta.apache.org/commons/httpclient</ulink>
for more info.</para>
<section id="remoting-httpinvoker-server">
<title>Exposing the service object</title>
<para>Setting up the HTTP invoker infrastructure for a service object
resembles closely the way you would do the same using Hessian or Burlap.
Just as Hessian support provides the
<classname>HessianServiceExporter</classname>, Spring's HttpInvoker
support provides the
<classname>org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter</classname>.</para>
<para>To expose the <literal>AccountService</literal> (mentioned above)
within a Spring Web MVC <classname>DispatcherServlet</classname>, the
following configuration needs to be in place in the dispatcher's
application context:</para>
<programlisting language="xml"><bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
</programlisting>
<para>Such an exporter definition will be exposed through the
<classname>DispatcherServlet</classname>'s standard mapping facilities,
as explained in the section on Hessian.</para>
<para>Alternatively, create an
<classname>HttpInvokerServiceExporter</classname> in your root
application context (e.g. in
<filename>'WEB-INF/applicationContext.xml'</filename>):</para>
<programlisting language="xml"><bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean></programlisting>
<para>In addition, define a corresponding servlet for this exporter in
<filename>'web.xml'</filename>, with the servlet name matching the bean
name of the target exporter:</para>
<programlisting language="xml"><servlet>
<servlet-name>accountExporter</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>accountExporter</servlet-name>
<url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping></programlisting>
<para>If you are running outside of a servlet container and are using
Sun's Java 6, then you can use the built-in HTTP server implementation.
You can configure the <classname>SimpleHttpServerFactoryBean</classname> together with a
<classname>SimpleHttpInvokerServiceExporter</classname> as is shown in this example:</para>
<programlisting language="xml"><bean name="accountExporter"
class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
<bean id="httpServer"
class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
<property name="contexts">
<util:map>
<entry key="/remoting/AccountService" value-ref="accountExporter"/>
</util:map>
</property>
<property name="port" value="8080" />
</bean>
</programlisting>
</section>
<section id="remoting-httpinvoker-client">
<title>Linking in the service at the client</title>
<para>Again, linking in the service from the client much resembles the
way you would do it when using Hessian or Burlap. Using a proxy, Spring
will be able to translate your calls to HTTP POST requests to the URL
pointing to the exported service.</para>
<programlisting language="xml"><bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
</programlisting>
<para>As mentioned before, you can choose what HTTP client you want to
use. By default, the <classname>HttpInvokerProxy</classname> uses the
J2SE HTTP functionality, but you can also use the Commons
<classname>HttpClient</classname> by setting the
<literal>httpInvokerRequestExecutor</literal> property:</para>
<programlisting language="xml"><property name="httpInvokerRequestExecutor">
<bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor"/>
</property>
</programlisting>
</section>
</section>
<section id="remoting-web-services">
<title>Web services</title>
<para>Spring provides full support for standard Java web services
APIs:</para>
<itemizedlist>
<listitem>
<para>Exposing web services using JAX-RPC</para>
</listitem>
<listitem>
<para>Accessing web services using JAX-RPC</para>
</listitem>
<listitem>
<para>Exposing web services using JAX-WS</para>
</listitem>
<listitem>
<para>Accessing web services using JAX-WS</para>
</listitem>
</itemizedlist>
<note>
<para>Why two standard Java web services APIs?</para>
<para>JAX-RPC 1.1 is the standard web service API in J2EE 1.4. As its
name indicates, it focuses on on RPC bindings, which became less and
less popular in the past couple of years. As a consequence, it has been
superseded by JAX-WS 2.0 in Java EE 5, being more flexible in terms of
bindings but also being heavily annotation-based. JAX-WS 2.1 is also
included in Java 6 (or more specifically, in Sun's JDK 1.6.0_04 and
above; previous Sun JDK 1.6.0 releases included JAX-WS 2.0), integrated
with the JDK's built-in HTTP server.</para>
<para>Spring can work with both standard Java web services APIs. On Java
EE 5 / Java 6, the obvious choice is JAX-WS. On J2EE 1.4 environments
that run on Java 5, you might have the option to plug in a JAX-WS
provider; check your Java EE server's documentation.</para>
</note>
<para>In addition to stock support for JAX-RPC and JAX-WS in Spring Core,
the Spring portfolio also features <ulink
url="http://www.springframework.org/spring-ws">Spring Web
Services</ulink>, a solution for contract-first, document-driven web
services - highly recommended for building modern, future-proof web
services.</para>
<section id="remoting-web-services-jaxrpc-export">
<title>Exposing servlet-based web services using JAX-RPC</title>
<para>Spring provides a convenience base class for JAX-RPC servlet
endpoint implementations -
<classname>ServletEndpointSupport</classname>. To expose our
<interfacename>AccountService</interfacename> we extend Spring's
<classname>ServletEndpointSupport</classname> class and implement our
business logic here, usually delegating the call to the business
layer.</para>
<programlisting language="java"><lineannotation>/**
* JAX-RPC compliant RemoteAccountService implementation that simply delegates
* to the AccountService implementation in the root web application context.
*
* This wrapper class is necessary because JAX-RPC requires working with dedicated
* endpoint classes. If an existing service needs to be exported, a wrapper that
* extends ServletEndpointSupport for simple application context access is
* the simplest JAX-RPC compliant way.
*
* This is the class registered with the server-side JAX-RPC implementation.
* In the case of Axis, this happens in "server-config.wsdd" respectively via
* deployment calls. The web service engine manages the lifecycle of instances
* of this class: A Spring application context can just be accessed here.
*/</lineannotation>import org.springframework.remoting.jaxrpc.ServletEndpointSupport;
public class AccountServiceEndpoint extends ServletEndpointSupport implements RemoteAccountService {
private AccountService biz;
protected void onInit() {
this.biz = (AccountService) getWebApplicationContext().getBean("accountService");
}
public void insertAccount(Account acc) throws RemoteException {
biz.insertAccount(acc);
}
public Account[] getAccounts(String name) throws RemoteException {
return biz.getAccounts(name);
}
}</programlisting>
<para>Our <classname>AccountServletEndpoint</classname> needs to run in
the same web application as the Spring context to allow for access to
Spring's facilities. In case of Axis, copy the
<classname>AxisServlet</classname> definition into your
<filename>'web.xml'</filename>, and set up the endpoint in
<filename>'server-config.wsdd'</filename> (or use the deploy tool). See
the sample application JPetStore where the
<interfacename>OrderService</interfacename> is exposed as a web service
using Axis.</para>
</section>
<section id="remoting-web-services-jaxrpc-access">
<title>Accessing web services using JAX-RPC</title>
<para>Spring provides two factory beans to create JAX-RPC web service
proxies, namely <classname>LocalJaxRpcServiceFactoryBean</classname> and
<classname>JaxRpcPortProxyFactoryBean</classname>. The former can only
return a JAX-RPC service class for us to work with. The latter is the
full-fledged version that can return a proxy that implements our
business service interface. In this example we use the latter to create
a proxy for the <interfacename>AccountService</interfacename> endpoint
we exposed in the previous section. You will see that Spring has great
support for web services requiring little coding efforts - most of the
setup is done in the Spring configuration file as usual:</para>
<programlisting language="xml"><bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
<property name="serviceInterface" value="example.RemoteAccountService"/>
<property name="wsdlDocumentUrl" value="http://localhost:8080/account/services/accountService?WSDL"/>
<property name="namespaceUri" value="http://localhost:8080/account/services/accountService"/>
<property name="serviceName" value="AccountService"/>
<property name="portName" value="AccountPort"/>
</bean></programlisting>
<para>Where <literal>serviceInterface</literal> is our remote business
interface the clients will use. <literal>wsdlDocumentUrl</literal> is
the URL for the WSDL file. Spring needs this at startup time to create
the JAX-RPC Service. <literal>namespaceUri</literal> corresponds to the
targetNamespace in the .wsdl file. <literal>serviceName</literal>
corresponds to the service name in the .wsdl file.
<literal>portName</literal> corresponds to the port name in the .wsdl
file.</para>
<para>Accessing the web service is now very easy as we have a bean
factory for it that will expose it as
<literal>RemoteAccountService</literal> interface. We can wire this up
in Spring:</para>
<programlisting language="xml"><bean id="client" class="example.AccountClientImpl">
...
<property name="service" ref="accountWebService"/>
</bean></programlisting>
<para>From the client code we can access the web service just as if it
was a normal class, except that it throws
<exceptionname>RemoteException</exceptionname>.</para>
<programlisting language="java">public class AccountClientImpl {
private RemoteAccountService service;
public void setService(RemoteAccountService service) {
this.service = service;
}
public void foo() {
try {
service.insertAccount(...);
}
catch (RemoteException ex) {
<lineannotation>// ouch</lineannotation>
}
}
}
</programlisting>
<para>We can get rid of the checked
<exceptionname>RemoteException</exceptionname> since Spring supports
automatic conversion to its corresponding unchecked
<exceptionname>RemoteException</exceptionname>. This requires that we
provide a non-RMI interface also. Our configuration is now:</para>
<programlisting language="xml"><bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
<property name="serviceInterface" value="example.AccountService"/>
<property name="portInterface" value="example.RemoteAccountService"/>
...
</bean></programlisting>
<para>Where <literal>serviceInterface</literal> is changed to our non
RMI interface. Our RMI interface is now defined using the property
<literal>portInterface</literal>. Our client code can now avoid handling
<exceptionname>java.rmi.RemoteException</exceptionname>:</para>
<programlisting language="java">public class AccountClientImpl {
private AccountService service;
public void setService(AccountService service) {
this.service = service;
}
public void foo() {
service.insertAccount(...);
}
}</programlisting>
<para>Note that you can also drop the "portInterface" part and specify a
plain business interface as "serviceInterface". In this case,
<classname>JaxRpcPortProxyFactoryBean</classname> will automatically
switch to the JAX-RPC "Dynamic Invocation Interface", performing dynamic
invocations without a fixed port stub. The advantage is that you don't
even need to have an RMI-compliant Java port interface around (e.g. in
case of a non-Java target web service); all you need is a matching
business interface. Check out
<classname>JaxRpcPortProxyFactoryBean</classname>'s javadoc for details
on the runtime implications.</para>
</section>
<section id="remoting-web-services-jaxrpc-mapping-registration">
<title>Registering JAX-RPC Bean Mappings</title>
<para>To transfer complex objects over the wire such as
<classname>Account</classname> we must register bean mappings on the
client side.</para>
<note>
<para>On the server side using Axis registering bean mappings is
usually done in the <filename>'server-config.wsdd'</filename>
file.</para>
</note>
<para>We will use Axis to register bean mappings on the client side. To
do this we need to register the bean mappings programmatically:</para>
<programlisting language="java">public class AxisPortProxyFactoryBean extends JaxRpcPortProxyFactoryBean {
protected void postProcessJaxRpcService(Service service) {
TypeMappingRegistry registry = service.getTypeMappingRegistry();
TypeMapping mapping = registry.createTypeMapping();
registerBeanMapping(mapping, Account.class, "Account");
registry.register("http://schemas.xmlsoap.org/soap/encoding/", mapping);
}
protected void registerBeanMapping(TypeMapping mapping, Class type, String name) {
QName qName = new QName("http://localhost:8080/account/services/accountService", name);
mapping.register(type, qName,
new BeanSerializerFactory(type, qName),
new BeanDeserializerFactory(type, qName));
}
}</programlisting>
</section>
<section id="remoting-web-services-jaxrpc-handler-registration">
<title>Registering your own JAX-RPC Handler</title>
<para>In this section we will register our own
<interfacename>javax.rpc.xml.handler.Handler</interfacename> to the web
service proxy where we can do custom code before the SOAP message is
sent over the wire. The <interfacename>Handler</interfacename> is a
callback interface. There is a convenience base class provided in
<filename class="libraryfile">jaxrpc.jar</filename>, namely
<classname>javax.rpc.xml.handler.GenericHandler</classname> that we will
extend:</para>
<programlisting language="java">public class AccountHandler extends GenericHandler {
public QName[] getHeaders() {
return null;
}
public boolean handleRequest(MessageContext context) {
SOAPMessageContext smc = (SOAPMessageContext) context;
SOAPMessage msg = smc.getMessage();
try {
SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.getHeader();
...
}
catch (SOAPException ex) {
throw new JAXRPCException(ex);
}
return true;
}
}</programlisting>
<para>What we need to do now is to register our AccountHandler to
JAX-RPC Service so it would invoke
<methodname>handleRequest(..)</methodname> before the message is sent
over the wire. Spring has at this time of writing no declarative support
for registering handlers, so we must use the programmatic approach.
However Spring has made it very easy for us to do this as we can
override the <methodname>postProcessJaxRpcService(..)</methodname>
method that is designed for this:</para>
<programlisting language="java">public class AccountHandlerJaxRpcPortProxyFactoryBean extends JaxRpcPortProxyFactoryBean {
protected void postProcessJaxRpcService(Service service) {
QName port = new QName(this.getNamespaceUri(), this.getPortName());
List list = service.getHandlerRegistry().getHandlerChain(port);
list.add(new HandlerInfo(AccountHandler.class, null, null));
logger.info("Registered JAX-RPC AccountHandler on port " + port);
}
}</programlisting>
<para>The last thing we must remember to do is to change the Spring
configuration to use our factory bean:</para>
<programlisting language="xml"><bean id="accountWebService" class="example.AccountHandlerJaxRpcPortProxyFactoryBean">
...
</bean></programlisting>
</section>
<section id="remoting-web-services-jaxws-export-servlet">
<title>Exposing servlet-based web services using JAX-WS</title>
<para>Spring provides a convenient base class for JAX-WS servlet
endpoint implementations -
<classname>SpringBeanAutowiringSupport</classname>. To expose our
<interfacename>AccountService</interfacename> we extend Spring's
<classname>SpringBeanAutowiringSupport</classname> class and implement
our business logic here, usually delegating the call to the business
layer. We'll simply use Spring 2.5's <literal>@Autowired</literal>
annotation for expressing such dependencies on Spring-managed
beans.</para>
<programlisting language="java"><lineannotation>/**
* JAX-WS compliant AccountService implementation that simply delegates
* to the AccountService implementation in the root web application context.
*
* This wrapper class is necessary because JAX-WS requires working with dedicated
* endpoint classes. If an existing service needs to be exported, a wrapper that
* extends SpringBeanAutowiringSupport for simple Spring bean autowiring (through
* the @Autowired annotation) is the simplest JAX-WS compliant way.
*
* This is the class registered with the server-side JAX-WS implementation.
* In the case of a Java EE 5 server, this would simply be defined as a servlet
* in web.xml, with the server detecting that this is a JAX-WS endpoint and reacting
* accordingly. The servlet name usually needs to match the specified WS service name.
*
* The web service engine manages the lifecycle of instances of this class.
* Spring bean references will just be wired in here.
*/</lineannotation>
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
@WebService(serviceName="AccountService")
public class AccountServiceEndpoint extends SpringBeanAutowiringSupport {
@Autowired
private AccountService biz;
@WebMethod
public void insertAccount(Account acc) {
biz.insertAccount(acc);
}
@WebMethod
public Account[] getAccounts(String name) {
return biz.getAccounts(name);
}
}</programlisting>
<para>Our <classname>AccountServletEndpoint</classname> needs to run in
the same web application as the Spring context to allow for access to
Spring's facilities. This is the case by default in Java EE 5
environments, using the standard contract for JAX-WS servlet endpoint
deployment. See Java EE 5 web service tutorials for details.</para>
</section>
<section id="remoting-web-services-jaxws-export-standalone">
<title>Exporting standalone web services using JAX-WS</title>
<para>The built-in JAX-WS provider that comes with Sun's JDK 1.6
supports exposure of web services using the built-in HTTP server that's
included in JDK 1.6 as well. Spring's
<classname>SimpleJaxWsServiceExporter</classname> detects all
<literal>@WebService</literal> annotated beans in the Spring application
context, exporting them through the default JAX-WS server (the JDK 1.6
HTTP server).</para>
<para>In this scenario, the endpoint instances are defined and managed
as Spring beans themselves; they will be registered with the JAX-WS
engine but their lifecycle will be up to the Spring application context.
This means that Spring functionality like explicit dependency injection
may be applied to the endpoint instances. Of course, annotation-driven
injection through <literal>@Autowired</literal> will work as
well.</para>
<programlisting language="xml"><bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
<property name="baseAddress" value="http://localhost:8080/"/>
</bean>
<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
...
</bean>
...</programlisting>
<para>The <classname>AccountServiceEndpoint</classname> may derive from
Spring's <classname>SpringBeanAutowiringSupport</classname> but doesn't
have to since the endpoint is a fully Spring-managed bean here. This
means that the endpoint implementation may look like as follows, without
any superclass declared - and Spring's <literal>@Autowired</literal>
configuration annotation still being honored:</para>
<programlisting language="java">@WebService(serviceName="AccountService")
public class AccountServiceEndpoint {
@Autowired
private AccountService biz;
@WebMethod
public void insertAccount(Account acc) {
biz.insertAccount(acc);
}
@WebMethod
public List<Account> getAccounts(String name) {
return biz.getAccounts(name);
}
}</programlisting>
</section>
<section id="remoting-web-services-jaxws-export-ri">
<title>Exporting web services using the JAX-WS RI's Spring
support</title>
<para>Sun's JAX-WS RI, developed as part of the GlassFish project, ships
Spring support as part of its JAX-WS Commons project. This allows for
defining JAX-WS endpoints as Spring-managed beans, similar to the
standalone mode discussed in the previous section - but this time in a
Servlet environment. <emphasis>Note that this is not portable in a Java
EE 5 environment; it is mainly intended for non-EE environments such as
Tomcat, embedding the JAX-WS RI as part of the web
application.</emphasis></para>
<para>The difference to the standard style of exporting servlet-based
endpoints is that the lifecycle of the endpoint instances themselves
will be managed by Spring here, and that there will be only one JAX-WS
servlet defined in <literal>web.xml</literal>. With the standard Java EE
5 style (as illustrated above), you'll have one servlet definition per
service endpoint, with each endpoint typically delegating to Spring
beans (through the use of <literal>@Autowired</literal>, as shown
above).</para>
<para>Check out <ulink
url="https://jax-ws-commons.dev.java.net/spring/">https://jax-ws-commons.dev.java.net/spring/</ulink>
for the details on setup and usage style.</para>
</section>
<section id="remoting-web-services-jaxws-access">
<title>Accessing web services using JAX-WS</title>
<para>Analogous to the JAX-RPC support, Spring provides two factory
beans to create JAX-WS web service proxies, namely
<classname>LocalJaxWsServiceFactoryBean</classname> and
<classname>JaxWsPortProxyFactoryBean</classname>. The former can only
return a JAX-WS service class for us to work with. The latter is the
full-fledged version that can return a proxy that implements our
business service interface. In this example we use the latter to create
a proxy for the <interfacename>AccountService</interfacename> endpoint
(again):</para>
<programlisting language="xml"><bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="example.AccountService"/>
<property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/>
<property name="namespaceUri" value="http://example/"/>
<property name="serviceName" value="AccountService"/>
<property name="portName" value="AccountServiceEndpointPort"/>
</bean></programlisting>
<para>Where <literal>serviceInterface</literal> is our business
interface the clients will use. <literal>wsdlDocumentUrl</literal> is
the URL for the WSDL file. Spring needs this a startup time to create
the JAX-WS Service. <literal>namespaceUri</literal> corresponds to the
targetNamespace in the .wsdl file. <literal>serviceName</literal>
corresponds to the service name in the .wsdl file.
<literal>portName</literal> corresponds to the port name in the .wsdl
file.</para>
<para>Accessing the web service is now very easy as we have a bean
factory for it that will expose it as <literal>AccountService</literal>
interface. We can wire this up in Spring:</para>
<programlisting language="xml"><bean id="client" class="example.AccountClientImpl">
...
<property name="service" ref="accountWebService"/>
</bean></programlisting>
<para>From the client code we can access the web service just as if it
was a normal class:</para>
<programlisting language="java">public class AccountClientImpl {
private AccountService service;
public void setService(AccountService service) {
this.service = service;
}
public void foo() {
service.insertAccount(...);
}
}</programlisting>
<para><emphasis>NOTE:</emphasis> The above is slightly simplified in
that JAX-WS requires endpoint interfaces and implementation classes to
be annotated with <literal>@WebService</literal>,
<literal>@SOAPBinding</literal> etc annotations. This means that you
cannot (easily) use plain Java interfaces and implementation classes as
JAX-WS endpoint artifacts; you need to annotate them accordingly first.
Check the JAX-WS documentation for details on those requirements.</para>
</section>
</section>
<section id="remoting-jms">
<title>JMS</title>