General Remarks
- We suggest to read the following
tutorials before you start implementing:
- Study the JacORB Programming
Guide. Specifically chapters 4 and 5 are interesting for this lab.
You can find it in the JacORB distribution in
doc/ProgrammingGuide.pdf. - The JacORBbank demo shows
how to work with implicit transactions and is a good reference for this lab.
You can find it in the JacORB distribution in
demo/bank/transaction/implicit. - IDL overview: Provides a good overview over the Interface Definition Language.
- Java IDL Mapping: Describes the mapping from IDL to Java.
- Java CORBA Introduction: This is a nice introduction for using CORBA with Java. Note that it's not JacORB specific!
- Study the JacORB Programming
Guide. Specifically chapters 4 and 5 are interesting for this lab.
You can find it in the JacORB distribution in
- Be sure to check the Tricky Parts section for questions!
Submission Guide
Submission
- Upload your solution as a ZIP file. Please submit only the sources including your build and idl file (see below) of your solution (not the compiled class files and no third-party libraries).
- Your submission must compile and run on our laptops. Use and complete the provided ant template.
Interviews
- After the submission deadline, there will be a mandatory interview.
- During the interview, you will be asked about the solution that you uploaded. In the interview you need to explain your code, design and architecture in detail.
- Be prepared that we will held reviews every lesson to get some additional
marks.
Description
In this assignment you will learn:
- the basics of CORBA (Common Object Request Broker Architecture)
- how to write a CORBA solution by starting with an IDL (Interface Description Language)
- how to generate Java artifacts from CORBA
- how to implement simple CORBA objects and clients
- how to use implicit transactions in CORBA
Overview
CORBA objects are hosted by server applications that provide one or more object instances to clients. The communication of both the client and the server is done by using so called ORBs (Object Request Brokers) that deal with marshaling (encoding arguments in a platform independent-format) and unmarshaling (decoding platform-independent data type representations into platform specific formats) and invocation of CORBA operations. One primary advantage of CORBA compared to other technologies such as RMI, .NET Remoting, or Web services is its coverage of services. That is, CORBA supports the full set of additional services that are required in most distributed system environments such as naming and trading, transaction and synchronization, notification and security services.
In this assignment we will use CORBA to implement a short messaging service (sms) infrastructure for telephone companies (telcos). Since it must be possible for customers of one telco to send messages to customers of other telcos, these telcos must be able to communicate with each other. Each telco can have its own IT infrastructure, possibly implemented in different languages on different platforms. Therefore, a standard communication technology has to be used for integrating these heterogeneous solutions. We choose CORBA to realize such a telco system.
Domain assumptions:
- A telco is a telephone company which provides its
customers a service to send short messages. Each telco has a name and its own
unique prefix which consists of several digits (but at least one) (e.g.
"0664"). - Messages contain text of arbitrary length (quite contradictory to sms, but not relevant for our purpose) and get a timestamp on sending. Messages may be sent to multiple recipients, who may be customers of different telcos.
- A telco internal number is a telephone number that is
only unique within a telco's scope and consists of several digits (but at
least one) (e.g. "
123456"). A full number is a telephone number that is unique accross telcos, i.e., it consists of a prefix and a telco internal number (e.g."0664/123456"). - Customer authentication is based on the telco internal number together with a four digit PIN code.
Architectural considerations:
In this
assignment we won't have such a simple client-server-architecture as before.
Instead there might be several different telcos, that have to communicate with
other telcos over a well defined, public interface and with their clients over a
telco specific interface.
So each telco has to serve two purposes: on the one side it has to deal with messages sent by its own customers and potentially has to forward it to other telcos, on the other side it must be able to handle messages coming from other telcos and forward them to its own customers.
So customers may login, logout and send messages using the
internal interface of their telco. "Internal" in this context
means "telco internal", therefore this communication could be handled using any
possible protocol or implemented in any programming language (we will also use
CORBA and Java for this).
It first gets exciting when the telco server has to
communicate with other telcos (for example if a recipient of a message is not a
customer of the sender's telco) over a well defined public
interface, that each telco has to support. To make it even more interesting, the
delivery of the message to all recipients must be handled within an (implicit)
CORBA transaction, i.e., either all recipients receive the message, or
none.
To sum it up, each telco has an internal interface for serving its own
customer requests, and an external interface for forwarding incoming messages to
its own customers.
So when a message is received on a telco, it has to deliver the message to the recipients. If the concerned recipient is currently online, the telco has to immediately forward it (therefore we need client callbacks again), otherwise it has to store it temporarily and transmit it the next time the recipient goes online. However, it's not necessary to store the state persistently when the telco is shut down.
As already said in the last assignment, most distributed object
frameworks provide a naming service, which allows binding/looking up remote
references to/by simple names. CORBA provides one (org.omg.CosNaming.NamingContextExt), and we will use it for
binding telco references to their prefixes, allowing customers to find their
telco and telcos to find other telcos. As you will see this happens quite
similar as with the java.rmi.registry.Registry in RMI.
The following figure shows an example scenario:
![]() Figure 1: Telco Architecture |
All operations displayed in red
have to be executed within one single transaction (started in
7'), the one shown using a dashed line is a notification (thus
called on the client callback object). The mailbox itself is not a stand-alone
communication partner (such as client or server) - it's meant as a server-side
data structure for storing missed messages of a single customer (anyway, you may
implement this requirement however you want). Please note that the order of some
operations (e.g. 9/10/11) is interchangeable and solely depends on your
implementation decisions.
Server
In this assignment the server is simply a specific implementation of a telco. It allows customers to login, send sms and logout, as well as other telcos to transmit messages to this telco's recipients.
Arguments
Your server program shall expect exactly one argument (if missing, print a usage message and exit):
fileName: specifies the name for a properties file that can be found on the classpath.
The properties file then should be read in from the
classpath (see the hint section for details). The format of properties
file looks like the following:
#prefix for the telco, also used for binding it to name
serviceprefix:0664#name of the
telconame:B2#customer data follows (telco
internal number:pin
pairs):123456:1234234567:2345345678:3456...
An example file is provided and can be downloaded here. It contains the prefix (which shall
be used for binding the telco object) and name of the telco to be
started, as well as all customer data in form of telco internal number and pin
code pairs. You can expect that there's no other text in it, so simply filter
out the prefix and name and then read all accounts.
Interfaces
In CORBA you always have to define your interfaces in IDL files, thus, create
the file telco.idl in your source folder (the ant template provided expects this path and name) and
specify your interfaces, data structures etc. there. This specification is
platform neutral and can be translated by IDL compilers to platform specific
code (in our case: Java).
Public telco interface
This is the interface
every telco has to implement to assure compatibility with other telcos.
Normally, this interface would be given to you by some standardization group,
but for our lab you also have to define it.
Required functionality:
Get the nameof the telco.Get the prefixof the telco.Deliver message within a transaction:
Allows other telcos to submit messages (consisting of text, the sender's full number and sending timestamp) to one or several recipients of this telco (in form of telco internal numbers). The telco has to forward the message to all online recipients and store it temporarily for offline recipients. However, both ways must be implemented transaction aware (see this for help)! In case a recipient is unknown or any internal error occurs the telco may raise an exception.
Internal telco interface
Extend the public
interface to implement your own telco service. For any operation but login the
client must be already logged in - you can check this either by using a session
identifier or by letting the client submit its user name and password for each
operation).
Required functionality:
Login of a customer:
The client/customer has to supply its telco internal number, pin code and a callback object (see client description). The server has to check whether the submitted values are valid and return all missed messages.Logout of a customer:
From now on the server has to store any incoming message for this customer until the next time he/she goes online.Send message as a single transaction:
Lets the client send a message. The server has to create a transaction, possibly deliver the message to other telcos, possibly store the message for its own offline recipients and deliver it to its own online recipients. In case of any error (such as unknown number, unknown telco prefix or any failure, also of other telcos) the transaction must be rolled back, otherwise committed. You shall use the implicit transaction propagation model as described here. Note that also the sender may be a recipient.
Implementation details
On startup you first have to read in the properties file. Then create your
telco object, make it remotely available by exporting it and bind it to the
naming service using a NameComponent
containing the prefix as id and "telco" as kind.
When a customer wants to send a message, your telco
implementation has to create a transaction (see the JacORB bank example). Then it has to map recipients to
telcos (using the full number's prefix) and look up the telcos in the name
service (again by supplying the prefix). You shall cache already looked up telco
references to avoid unnecessary lookups. If any problem occurs (such as telco
could not be found in name service, telco does not know recipient number), you
have to rollback the transaction, otherwise commit it. Note that also already
looked up telcos might have gone offline - if a
org.omg.CORBA.TRANSIENT or org.omg.CORBA.COMM_FAILURE
exception occurs you may assume the telco is not reachable and have to rollback
the transaction.
When the telco itself is part of a transaction (either when
called by another telco or when a recipient of a message sent by an own customer
is also an own customer), you first have to check whether such a number is
known. If not, throw an appropriate exception (which will cause the transaction
to rollback). If the recipient is currently online you have to transmit the
message directly over the client callback (see below), otherwise store the
message. Clients may also go offline without informing the server appropriately
(for example if the computer crashes or the connection is lost). You don’t have
to detect this at once, but the next time the server is trying to contact the
client to deliver a message: if a org.omg.CORBA.TRANSIENT or
org.omg.CORBA.COMM_FAILURE exception occurs you may assume the
client is offline. Therefore update the client's state and store the message to
deliver it the next time the client goes online again.
As in the last assignment the telco has to manage state across several threads (they are managed by JacORB transparently). This again makes synchronization necessary when accessing your shared data structures!
If your server is ready for handling requests print
“Server <prefix> up. Hit enter to exit.” to the console and
implement this behavior. On exit unbind the telco corba object from the naming
service and also call ORB.shutdown(), otherwise your program might
not shut down – again you must not use System.exit().
Client
In this part you have to build an interactive command line client application for your telco implementation.
Arguments
Again only one argument has to be specified (if missing, print a usage message and exit):
prefix: the prefix of the telco to contact. The supplied value will be used for resolving the telco object in the naming service.
Callback interface
As you've already noticed, a telco has to inform its online recipients
immediately on incoming messages. If you've solved the last assignment, you are
already familiar with the callback concept - in CORBA it basically works exactly
the same way as in RMI. To make it short: you have to define an interface in
your IDL file (telco.idl) which your client has to implement.
Required functionality:
Receive message within a transaction:
This method can be called by the telco to inform the client about a message. The client has to output the message to the console, containing the sending time, the sender's full number and the text (e.g. "20.10.2008 @ 12:01 | 0664/123456: This is a test message."). Please note that the client notification must also be part of the CORBA transaction originally issued by the sender's telco.
Implementation details
On startup of your program you first have to look up your telco in the name
service using the specified prefix. Note that you can only cast the
looked up org.omg.CORBA.Object instance by using the
*Helper.narrow() method. If the telco was looked up successfully
print "Contacted telco "<telcoName>" (<telcoPrefix>)
successfully." to the console. Afterwards export your client callback
object like you exported your telco server object. The only difference is that
you should not bind it to the name service, but instead simply pass it as
parameter for the login command.
Interactive commands
The following commands must be supported by your client application. Take
care of handling invalid commands and arguments and provide usage messages in
these cases. Print meaningful error messages whenever the server throws an
“expected” exception (such as “Invalid login data.” when trying to
login with an invalid number/pin combination). Also print success messages if an
operation was executed successfully.
Note that you first have to login before
sending sms (and, obviously, before logging out).
login <telcoInternalNumber> <pin>
Logs the user in. The telco server has to return all missed messages. The output should include the same information as displayed below::> login 123456 1234Logged in successfully.Missed messages:
20.10.2008 @ 12:01 | 0699/123456: This is a test message. 20.10.2008 @ 12:05 | 0699/123456: This is also an extremely exciting test message.
logout
Logs out the currently logged in user.sendSms "<text>" [<telPrefix>/<telNumber>]+
Sends an sms containing the specified text (all text between (exclusive) the first two occuring quotes)) to a list (containing at least one) of space separated recipients (in form of full numbers in the specified format).:> sendSms "This is a test message." 0664/123456 0664/234567Message sent successfully.stop
Stops the client application. If a user is currently logged in you have to log it out implicitly. Again you may not useSystem.exit(), but have to orderly close all acquired resources (shut down the ORB).
Ant template
As in the last assignment we provide a template build file (build.xml) in which you only have to adjust some class
names. Put your source into the subdirectory "src", place the
*.properties files and telco.idl into the
"src" directory and move on the command line to the directory where
the build file is located.
Simply type "ant idl-compile" for idl compilation
(compiles the telco.idl file) and "ant" for java
compilation (this task also copies your .properties files to the
build directory).
Type "ant run-server -Dprops=b2" to start a server
reading from b2.properties. In the provided build file an argument
is expected for the server (<arg
value="${props}.properties"/>). By specifying the
-Dprops=b2 option, ant actually passes "b2.properties"
as an argument to your server.
Type "ant run-client -Dprefix=0664" to start a
client that connects to a telco bound to 0664 (by substituting
<arg value="${prefix}"/> in the build file by
"0664").
Put the src directory including telco.idl and
build.xml into your submission.
Note that it's
absolutely required that we are able to start your programs
with these predefined commands!
Transactions help
How to provide transaction support in your telco/client
As the deliver message operation of the public telco interface and the
message received operation of the client callback interface have to be executed
within transactions, the following problem arises:
According to the OMG Transaction Service Specfication a resource may
only be involved in one transaction at the same time. However, we want our
client and especially our telco to be able to receive messages from different
transactions concurrently, otherwise the performance would be quite bad.
So don't let your public telco interface and client callback
directly extend CosTransaction::Resource and CosTransaction::TransactionalObject, and instead create an
additional interface that extends these both and only provides a simple method
that takes a message parameter (containing sending time, sender's full number
and text).
On the client's side it becomes now simple. Provide an implementation for the specified resource interface, which on commit simply outputs the received message. In your callback you then create a new instance of this for each incoming request, hand over the message, and register it with the coordinator.
On the telco's side it's a bit more complicated, since there might be several recipients that might be online or offline. If a recipient is online - there's no problem, just forward it to the callback object (which handles the transaction as described above). But if we have to store it for a recipient, we also have to consider the transaction semantics. Therefore also provide an implementation for the specified resource interface, which on commit stores the message to the recipient's mailbox. In your telco do the same as on the client's side (create resource, hand over message, register it with coordinator).
For your information:
The CosTransaction::TransactionalObject is just a markup interface and
hence has no operations at all (primary a historic relict from previous CORBA
standards). However, you have to implement the 5 operations of the
Resource interface. You can skip implementing the
forget() operation and implement the
commit_one_phase() operation as in the JacORB sample. The prepare() method shall
return one of the three values Vote.VoteCommit, Vote.VoteRollback, Vote.VoteReadOnly depending if the changes shall be
committed, or rolled back. Vote.VoteReadOnly shall be used if there were no
changes at all.
Implicit transactions
Using implicit transactions means that transaction contexts storing the current state of a transaction are not directly visible in client or server code but is automatically added by so-called interceptors - in CORBA interceptors hook in requests and may modify the transmitted data such as arguments or may add service contexts such as in our case.
To inform an ORB that it shall use the already predefined
interceptor for implicit transactions the following value for the property
org.omg.PortableInterceptor.ORBInitializerClass.TSClientInit must be set during the ORB
initialization: org.jacorb.transaction.TransactionInitializer. This is a class that
registers an object of type org.omg.CosTransactions.Current as an initial reference
named TransactionCurrent. This
reference can then be retrieved with
orb.resolve_initial_references. The provided ant template already sets this property (see the
run-server and run-client task).
The begin operation of the org.omg.CosTransactions.Current interface may be used to
initiate a transaction. The timeout shall be set to a value of 40. This has to
be set before you begin a transaction. After a transaction has begun you can
call whatever transaction aware functionality of CORBA objects you want. The
transaction context is transmitted implicitly. For rolling back a transaction
you have to call the rollback function (surprisingly). Committing
the transaction is done via the commit operation (use true as
argument). Never kill a server (regardless how) that has started a transaction
before the timeout has passed. When you do manual tests you can set the timeout
to a smaller value.
From transaction aware objects you have to use the
org.omg.CosTransactions.Current
interface, too. However, this time you shall receive the org.omg.CosTransactions.Control interface via the Current's
get_control() operation. Control supports two different
functionalities: returning a Coordinator (with
get_coordinator()) and returning a Terminator (with
get_terminator()). In transaction aware operations you have to
register the transaction aware CORBA object with the coordinator's
register_resource() operation. The Resource's
operations (prepare, rollback, commit) are then used automatically when the
transaction initiator calls commit or rollback. In case of any TransactionServer related exception (example:
Inactive) throw the runtime exception
org.omg.CORBA.TRANSACTION_ROLLEDBACK. To get the exact syntax of
the transaction server related operations take a look at the CosTransactions.idl or study the OMG Transaction Service Specification.
IDL compilation
For compiling your IDL file, which uses transaction service interfaces, you have to
- write
#include <CosTransactions.idl>at the top of your IDL file to include the interface definitions - and therefore tell the JacORB IDL compiler
where to find the required IDL file
<CosTransactions.idl>: Use the-Ioption (e.g.-I/opt/jacorb/idl/omg/) to do this. If you use our ant template this option is already included (take a look at theidl-compiletarget).
Hints & Tricky Parts
- Study the JacORB programmer's guide, in particular chapter 4 - Getting Started - explains how to program a CORBA server and a CORBA client with JacORB. Chapter 5 explains how to access the name service. Chapter 4 contains everything you need to implement your server and client (except the transactional stuff).
- In order to use JacORB instead of the built-in
(and incomplete) ORB of SUN that is provided with Java, you have to provide
two arguments to the Java VM with
-D:
-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORBand-Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton. You also have to include all the JacORB JAR files (located in the lib directory of the JacORB installation) into the CLASSPATH. Instead of manually applying these settings, you can also use thejacoshell script that is located in the$JACORB_HOME/bindirectory. This script invokes Java with the appropriate VM arguments and classpath settings.
We suggest using the provided ant template, which already includes these settings.
- JacORB services:
You will need to start the naming and transaction service to accomplish this task. All described service commands are located in the$JACORB_HOME/bindirectory. We recommend using the predefined tasks of the provided ant template which clearly simplify the configuration and startup of these services.
- Naming service:
You need the naming service to bind and lookup your CORBA objects (this is quite similar to the RMI registry from the last lab).
Usage:ns -Djacorb.naming.ior_filename=$HOME/NS_REForant run-nsIf you don't use the provided ant target, you have to copy the jacorb_properties.template from$JACORB_HOME/etcto the directory where you execute your servers/clients and rename it to jacorb.properties. Then edit theORBInitRef.NameServiceentry and set it to the appropriate path (e.g. ORBInitRef.NameService=file:///dslab/home/dslab/dslabXXX/NS_REF), so your programs are able to locate the naming service. There you can also edit a lot of other configuration parameters when invoking JacORB, although you don't have to. - Transaction service:
You first have to start the naming service as described above.
Usage:ts -ORBInitRefNameService=orfile://$HOME/NS_REFant run-ts - Naming manager:
If you are working at home (this one's a GUI application) you can use this tool to view the objects bound to the naming service. This is helpful if you are unsure about your binding code (the transaction service is also visible there if it was started successfully).
Usage:nmg -ORBInitRefNameService=orfile://$HOME/NS_REFant run-nmg
- Naming service:
- Using JacORB at home:
- Download JacORB. If you want to build the JacORB API documentation (see below) you have to select the source distribution, otherwise the binary one is sufficient.
- Set the environment entry
$JACORB_HOME. - If you don't want to use the predefined ant targets you have to call
"ant jaco"inJACORB_HOMEand should include$JACORB_HOME/bininto your$PATHvariable.
- Reading in a properties file from the classpath (without
exception handling):
java.io.InputStream is = ClassLoader.getSystemResourceAsStream("b2.properties");if (is != null) {-
java.util.Properties props = new java.util.Properties();
try {props.load(is);String prefix = props.getProperty("prefix");...} finally {is.close();}} else {System.err.println("Properties file not found!");} -
- Submitting dates in CORBA: simply pass the time value as
returned by
java.util.Date.getTime()and reconstruct the date using thejava.util.Date(long time)constructor. Note that alongin Java is equivalent to along longin the IDL.
Further Reading Suggestions
- APIs:
- CORBA: Package API, ORB API, CORBA Object API
- CORBA Naming Service: Package API, NamingContextExt API
- CORBA Portable Server: Package API, POA API, Servant API
- JacORB and CORBA Transactions: you have to
call
"ant doc"inJACORB_HOME(works only with the source distribution). Afterwards the API can be found inJACORB_HOME/doc/api/- mainly theorg.omg.CosTransactionspackage will be of interest for you. - Properties: Properties API
- Specifiations
- OMG CORBA Specification: This is the original OMG CORBA Specification. You can use it as a reference if you are specially interested in CORBA.
- OMG Transaction Service Specfication: This is the original OMG Transaction Service Specification. In general, it is not necessary to read all of this. However, in Section 2 all involved CORBA interfaces are explained (as Resource, Control, Coordinator, Terminator).
- OMG IDL to Java Mapping Specification: The official OMG document about the mapping from IDL to Java.
- Tutorials:
- CORBA Callbacks: Simple tutorial for callbacks (not JacORB specific).
- Java CORBA Guide: Here you can find several informations about CORBA with Java; again this is not related to using JacORB.
- Proposed Literature:
- Advanced CORBA(R) Programming with C++ by by Michi Henning and Steve Vinoski
| Abgabetermin: | Mittwoch, 20. Januar 2010, 09:30 |
-Dateien/overview.png)