Date: Fri, 29 Mar 2024 10:24:01 +0000 (UTC) Message-ID: <1013213295.25005.1711707841662@ae5f4610bf64> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_25004_271916109.1711707841662" ------=_Part_25004_271916109.1711707841662 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
This page shows how to create an Ice application with Java.
On this page:
The first step in creating our Java application is to compile our Slice definition to gen= erate Java proxies and skeletons. You can compile the definition as follows= :
$ mkdir= generated $ slice2java --output-dir generated Printer.ice
The --output-dir
option instructs the compiler to place the=
generated files into the generated
directory. This avoids clu=
ttering the working directory with the generated files. The slice2jav=
a
compiler produces a number of Java source files from this definiti=
on. The exact contents of these files do not concern us for now =E2=80=94 t=
hey contain the generated code that corresponds to the Printer
=
interface we defined in Printer.ice
.
To implement our Printer
interface, we must create a servan=
t class. By convention, a servant class uses the name of its interface with=
an I
-suffix, so our servant class is called PrinterI and placed into a source file
PrinterI.java
:
public c= lass PrinterI extends Demo._PrinterDisp { public void printString(String s, Ice.Current cu= rrent) { System.out.println(s); } }
The PrinterI
class inherits from a base class called =
_PrinterDisp
, which is generated by the slice2java
comp=
iler. The base class is abstract and contains a printString
me=
thod that accepts a string for the printer to print and a parameter of type=
Ice.Current
=
. (For now we will ignore the Ice.Current
parameter.) Our impl=
ementation of the printString
method simply writes its argumen=
t to the terminal.
The remainder of the server code is in a source file called Server=
.java
, shown in full here:
public c= lass Server { public static void main(String[] args) { int status =3D&nb= sp;0; Ice.Communicator ic&nb= sp;=3D null; try { ic&= nbsp;=3D Ice.Util.initialize(args); Ice= .ObjectAdapter adapter =3D &nb= sp; ic.createObjectAdapterWithEndpoints("SimplePrinterAdap= ter", "default -p 10000"); Ice= .Object object =3D new PrinterI(); ada= pter.add(object, ic.stringToIdentity("SimplePrinter")); ada= pter.activate(); ic.= waitForShutdown(); } catch (Ice.Loca= lException e) { e.p= rintStackTrace(); sta= tus =3D 1; } catch (Exceptio= n e) { Sys= tem.err.println(e.getMessage()); sta= tus =3D 1; } if (ic !=3D = null) { //&= nbsp;Clean up // try= { &nb= sp; ic.destroy(); }&n= bsp;catch (Exception e) { &nb= sp; System.err.println(e.getMessage()); &nb= sp; status =3D 1; } } System.exit(status); } }
Note the general structure of the code:
public c= lass Server { public static void main(String[] args) { int status =3D&nb= sp;0; Ice.Communicator ic&nb= sp;=3D null; try { // = Server implementation here... } catch (Ice.Loca= lException e) { e.p= rintStackTrace(); sta= tus =3D 1; } catch (Exceptio= n e) { Sys= tem.err.println(e.getMessage()); sta= tus =3D 1; } if (ic !=3D = null) { //&= nbsp;Clean up // try= { &nb= sp; ic.destroy(); }&n= bsp;catch (Exception e) { &nb= sp; System.err.println(e.getMessage()); &nb= sp; status =3D 1; } } System.exit(status); } }
The body of main
contains a try
block in which=
we place all the server code, followed by two catch
blocks. T=
he first block catches all exceptions that may be thrown by the Ice run tim=
e; the intent is that, if the code encounters an unexpected Ice run-time ex=
ception anywhere, the stack is unwound all the way back to main
, which prints the exception and then returns failure to the operating sys=
tem. The second block catches Exception
exceptions; the intent=
is that, if we encounter a fatal error condition somewhere in our code, we=
can simply throw an exception with an error message. Again, this unwinds t=
he stack all the way back to main
, which prints the error mess=
age and then returns failure to the operating system.
Before the code exits, it destroys the communicator (if one was created =
successfully). Doing this is essential in order to correctly finalize the I=
ce run time: the program must call destroy
on any com=
municator it has created; otherwise, undefined behavior results.
The body of our try
block contains the actual server code:<=
/p>
&= nbsp; ic =3D = ;Ice.Util.initialize(args); Ice= .ObjectAdapter adapter =3D &nb= sp; ic.createObjectAdapterWithEndpoints("SimplePrinterAdap= ter", "default -p 10000"); Ice= .Object object =3D new PrinterI(); ada= pter.add(object, ic.stringToIdentity("SimplePrinter")); ada= pter.activate(); ic.= waitForShutdown();
The code goes through the following steps:
Ice.Util.initialize. (We pass args
to this call because the server may have co=
mmand-line arguments that are of interest to the run time; for this example=
, the server does not require any command-line arguments.) The call to initialize
returns an Ice.Communicator
reference, whi=
ch is the main object in the Ice run time.createObjectAdapterWithEnd=
points
on the Communicator
instance. The arguments we p=
ass are "SimplePrinterAdapter"
(which is the name of the adapt=
er) and "default -p 10000"
, which instructs the adapter to lis=
ten for incoming requests using the default protocol (TCP/IP) at port numbe=
r 10000.Printer
interface by instantiating a Pri=
nterI
object.add
on the adapter; the arguments to add
are th=
e servant we have just instantiated, plus an identifier. In this case, the =
string "SimplePrinter"
is the name of the Ice object. (If we h=
ad multiple printers, each would have a different name or, more correctly, =
a different object identity.)activate
meth=
od. (The adapter is initially created in a holding state; this is useful if=
we have many servants that share the same adapter and do not want requests=
to be processed until after all the servants have been instantiated.)waitForShutdown
. This call suspends the c=
alling thread until the server implementation terminates, either by making =
a call to shut down the run time, or in response to a signal. (For now, we =
will simply interrupt the server on the command line when we no longer need=
it.)Note that, even though there is quite a bit of code here, that code is e=
ssentially the same for all servers. You can put that code into a helper cl=
ass and, thereafter, will not have to bother with it again. (Ice provides s=
uch a helper class, called Ice.Applicat=
ion
.) As far as actual application code is concerned, the server=
contains only a few lines: seven lines for the definition of the Pri=
nterI
class, plus three lines to instantiate a PrinterI
=
object and register it with the object adapter.
We can compile the server code as follows:
$ mkdir= classes $ javac -d classes -classpath classes:$ICE_HOME/li= b/Ice.jar \ Server.java PrinterI.java generated/Demo/*.java
This compiles both our application code and the code that was generated =
by the Slice compiler. We assume that the ICE_HOME
environment=
variable is set to the top-level directory containing the Ice run time. (F=
or example, if you have installed Ice in /opt/Ice
, set I=
CE_HOME
to that path.) Note that Ice for Java uses the ant build environment to control building of source code. (
ant
=
is similar to make
, but more flexible for Java applications.)=
You can have a look at the demo code that ships with Ice to see how to use=
this tool.
The client code, in Client.java
, looks very similar to the =
server. Here it is in full:
public c= lass Client { public static void main(String[] args) { int status =3D&nb= sp;0; Ice.Communicator ic&nb= sp;=3D null; try { ic&= nbsp;=3D Ice.Util.initialize(args); Ice= .ObjectPrx base =3D ic.stringToProxy("SimplePrinter:default&= nbsp;-p 10000"); Dem= o.PrinterPrx printer =3D Demo.PrinterPrxHelper.checkedCast(base); if&= nbsp;(printer =3D=3D null) &nb= sp; throw new Error("Invalid proxy"); pri= nter.printString("Hello World!"); } catch (Ice.Loca= lException e) { e.p= rintStackTrace(); sta= tus =3D 1; } catch (Exceptio= n e) { Sys= tem.err.println(e.getMessage()); sta= tus =3D 1; } if (ic !=3D = null) { //&= nbsp;Clean up // try= { &nb= sp; ic.destroy(); }&n= bsp;catch (Exception e) { &nb= sp; System.err.println(e.getMessage()); &nb= sp; status =3D 1; } } System.exit(status); } }
Note that the overall code layout is the same as for the server: we use =
the same try
and catch
blocks to deal with errors=
. The code in the try
block does the following:
Ice.=
Util.initialize
.stringToProxy
on the communicator, with the s=
tring "SimplePrinter:default -p 10000"
. Note that the string c=
ontains the object identity and the port number that were used by the serve=
r. (Obviously, hard-coding object identities and port numbers into our appl=
ications is a bad idea, but it will do for now; we will see more architectu=
rally sound ways of doing this when we discuss IceGrid.)stringToProxy
is of type Ice.O=
bjectPrx
, which is at the root of the inheritance tree for interface=
s and classes. But to actually talk to our printer, we need a proxy for a <=
code>Printer interface, not an Object
interface. To do =
this, we need to do a down-cast by calling PrinterPrxHelper.checkedCa=
st
. A checked cast sends a message to the server, effectively asking=
"is this a proxy for a Printer
interface?" If so, the call re=
turns a proxy of type Demo::Printer
; otherwise, if the proxy d=
enotes an interface of some other type, the call returns null.pr=
intString
method, passing it the time-honored "Hello World!"=
code> string. The server prints that string on its terminal.
Compiling the client looks much the same as for the server:
$ javac= -d classes -classpath classes:$ICE_HOME/lib/Ice.jar \ Client.java PrinterI.java generated/Demo/*.java
To run client and server, we first start the server in a separate window= :
$ java&= nbsp;Server
At this point, we won't see anything because the server simply waits for= a client to connect to it. We run the client in a different window:
$ java&= nbsp;Client $
The client runs and exits without producing any output; however, in the =
server window, we see the "Hello World!"
that is produced by t=
he printer. To get rid of the server, we interrupt it on the command line f=
or now. (We will see cleaner ways to terminate a server in our discussion o=
f Ice.Application
.)
If anything goes wrong, the client will print an error message. For exam= ple, if we run the client without having first started the server, we get s= omething like the following:
Ice.Connecti= onRefusedException error =3D 0 =09at IceInternal.ConnectRequestHandler.getConnection(ConnectRequestHand= ler.java:240) =09at IceInternal.ConnectRequestHandler.sendRequest(ConnectRequestHandle= r.java:138) =09at IceInternal.Outgoing.invoke(Outgoing.java:66) =09at Ice._ObjectDelM.ice_isA(_ObjectDelM.java:30) =09at Ice.ObjectPrxHelperBase.ice_isA(ObjectPrxHelperBase.java:111) =09at Ice.ObjectPrxHelperBase.ice_isA(ObjectPrxHelperBase.java:77) =09at Demo.HelloPrxHelper.checkedCast(HelloPrxHelper.java:228) =09at Client.run(Client.java:65) Caused by: java.net.ConnectException: Connection refused ...
Note that, to successfully run client and server, your Please have a look at the demo applications that ship with Ice for the d=
etails for your platform. CLASSPATH=
code> must include the Ice library and the classes directory, for example:<=
/p>
$ expor=
t CLASSPATH=3D$CLASSPATH:./classes:$ICE_HOME/lib/Ice.jar
See Also
Ice.Application
Class