In this lab, you have to implement three interfaces for your
server. The first one is IFileManager
which implements the server logic. Those of you who did Lab3
are already familiar with this interface. All the others
should read the Lab3 description first.
Before you start implementing your server, you should download
the DSGFileMan-lab4.jar that
contains all the classes and interfaces you need for implementing this lab.
Note that this JAR file differs from the one you used for implementing Lab3.
For example, it additionally contains
IFileManServer and
IRepository.
For solving Lab4, include this JAR file to your CLASSPATH.
Implementing IRepository
You may want to start by implementing the functionality for reading/writing
repositories (and repository documents) physically on disk. Therefore, you should implement the
IRepository
interface. Read the JavaDoc documentation of this interface to get more details about all methods.
Note that this interface is only a recommendation by us.
As it does not appear as parameter or return value in the IFileManager
interface,
you do not have to implement it. Thus, use it as guidance or feel free to implement your own repository ;-)
However, note that we can only provide little support if you do not use the IRepository
interface for implementing your repository.
On the client implemented in Lab3, you have only stored the
most recent document version. In contrast to this, the server
has to store all versions of the repository documents, to be able to
revert to older versions of a document.
However, it is up to you how you store the documents.
In any case, you have to assure that name, version, and content of the
documents are preserved.
One way to store the repositories and repository documents on the server is to use
a separate directory for each repository. For instance, repository dsgfm999_repo1 is stored in
$REPO_ROOT/dsgfm999_repo1, while repository dsgfm888_testrepository is stored in
$REPO_ROOT/dsgfm888_testrepository.
In these directories, you will store the repository configuration (owner, allowed users, etc.),
and all versions of the repository documents.
You may distinguish between different versions of a document by appending the version number
to the filename of the document. For instance, if you have three versions of document document.txt,
you may store them as document.txt.1, document.txt.2, document.txt.3.
Implementing IFileManager
After implementing the mechanism to physically store repositories and
repository documents on disk, you are ready to implement the
server logic.
Therefore, create a new public class called FileManagerImpl
and
place it in the package at.ac.tuwien.dslab.rmi.server
.
This class has to be a remote object (the server), and has to implement the IFileManager
interface.
You find a short description of each method below. The JavaDoc comments give you
a more detailed method specification (method parameters, which exceptions to throw, etc.).
We recommend that you carefully read the
JavaDoc documentation of this interface...
...so does the grading robot ;-)
Interface methods
Most of the methods take the name of the repository, and the user who wants to access
the repository as arguments. Each method has to check if the given repository really exists
(otherwise throw a RepositoryNotExistsException
),
and if the given user is valid and allowed to access the repository
(otherwise throw a UserAccessException
).
The createRepository()
method creates a repository on the server,
After creating the repository, the owner and all allowed users are able to access the repository.
The removeRepository()
method removes a repository from the server, if the given
user matches to the owner of the repository (i.e., only the owner is allowed to delete the repository).
The checkout()
method performs an initial checkout of the repository
(i.e., it returns the most recent versions of all repository documents).
The importDocument()
method imports a new document to the repository (i.e., the document
must not already exist in the repository). The version number of the document is set to 1
.
The commitDocument()
method commits a new version of a document to the repository
(i.e., the document must already exists in the repository). The new version of the document is only
committed, if the version number is correct. You therefore have to implement a simple versioning logic
(see below).
The removeDocument()
method deletes (all versions of) a document from the repository.
The updateAllDocuments()
method gets all documents from the repository.
Note that this method only returns the most recent versions of all documents.
The updateDocument()
method gets a specific document version from the repository.
The version number of the requested document is taken as argument. If the version number is -1
,
the method returns the most recent version of the document.
If the requested document does not exist in the repository, you should return null
.
As described above, the server provides a "watch dog" mechanism
that sends notifications to all registered listeners.
Thus, your server implementation needs a way to register and remove repository listeners;
see the methods addRepositoryListener()
and removeRespositoryListeners()
(we are sorry for this typo introduced in Lab3 :), respectively.
The repository listener is represented by the
IRepositoryListener
interface. If certain events occur, you have to invoke the appropriate methods of all registered listeners:
documentImported
: if a document was imported to the repository
documentCommitted
: if a document was committed to the repository
documentRemoved
: if a document was removed from to the repository
Please make sure that you read the reference on RMI callbacks from the reading suggestions below, in
order to understand remote events, callbacks, etc.
Constructor
In the constructor of FileManagerImpl
, initialize all required information
(e.g., a list of registered repository listeners).
In addition, you have to read the user account information from the user
file located on the CLASSPATH. The name of the user file can be found in the property file.
More details about this property file are explained in the IFileManServer
section below.
For testing purposes, you find a sample user account file test-accounts.txt
in your home directory. In addition, the file can also be found here.
The user account file contains all users that are allowed to access the DSGFileMan system.
Each line of this file contains one pair of account name and account password, which are separated by a colon (":").
Don't forget to add the sample user account file to your classpath! Moreover, add your own account information
(dsgfmXXX:<yourpassword>) to this file, to be able to test your server with your dsgfm-account.
Versioning logic
Your server has to implement the following simple versioning logic.
If a client wants to commit a document, there are three possibilities:
The version number of the document to commit can be
- smaller
than the version on the server: That means, the client does not have
the most recent version of the document and is therefore not allowed to
commit. In that case, throw a
FileManException
with a meaningful exception message.
- equal
to the version on the server: The document can be committed. Therefore,
the server stores the new document version, increments the version
number by
1
, and returns the new version number to the client.
- greater
than the version on the server: In fact, this should never happen
(i.e., the client is not working correctly). In that case, throw a
FileManException
with a meaningful exception message.
We are aware, that our versioning logic is quite simplified.
In contrast to more sophisticated tools such as Subversion or CVS,
we do not track the actual changes of document contents, but only
concentrate on the version number of the documents.
However, the main goal of Lab3 and Lab4 is to learn the basics of RMI,
and not how to resolve version conflicts.
Synchronization and null
values
Furthermore, you have to consider proper synchronization
mechanisms because multiple clients can connect concurrently.
Synchronize only the critical and necessary code blocks.
Please also make sure that your server implementation is
robust against remote method calls with "null" values. If you
encounter null values, throw appropriate exceptions (e.g.,
InvalidArgumentException
, etc).
Implementing IFileManServer
Finally, you have to provide a way to start and stop your server. Therefore, implement the IFileManServer
interface:
01 package at.ac.tuwien.dslab.rmi.common.interfaces;
02
03 import at.ac.tuwien.dslab.rmi.common.impl.FileManException;
04
05 /**
06 * The <code>IFileManServer</code> defines an interface
07 * for starting and stopping the RMI server.
08 *
09 * @author Florian Rosenberg
10 * @author Anton Michlmayr
11 */
12 public interface IFileManServer {
13
14 /**
15 * Starts the RMI server. Everything which is necessary to handle
16 * the correct startup of the server has to be done here.
17 * @throws FileManException If an error occurs while starting up the RMI server.
18 */
19 public void start() throws FileManException;
20
21 /**
22 * Stops the RMI server. Every cleanup which may be necessary has to be done here.
23 * @throws FileManException If an error occurs while stopping the RMI server.
24 */
25 public void stop() throws FileManException;
26
27 }
Your public implementation class of this interface must be called
FileManServerImpl
, and must be placed in the package
at.ac.tuwien.dslab.rmi.server
.
It has to provide a public
constructor that takes an
java.util.Properies
class as the only argument and throws
no exceptions.
In the constructor, you should build the appropriate RMI URL from
the property file given as argument. You will need this URL for binding the
file manager to the registry.
In the start()
method, create the IFileManager
instance with the necessary parameters and bind it to the registry.
If you do not implement the start()
method correctly, all
of your tests will fail since the grading robot cannot lookup the file
manager from the registry.
The stop()
method should unbind the RMI server
object, and unexport it from the registry.
For both operations, use the RMI URL you built in the constructor
(from the property file). If you do not use this RMI URL, the grading
robot will not be able to lookup your file manager, and your tests will fail!
Furthermore, the FileManServerImpl
class should
provide a main()
method for starting up the server.
You have to take the property file name (i.e., file name and path)
as the first command-line argument.
In the method body, instantiate your server with the given property file.
Then, start the server by invoking the start()
method.
Property file
The sample property file DSGFileMan.properties
defines the following properties:
RMI_HOSTNAME=pizza.dslab.tuwien.ac.at
RMI_PORT=1099
RMI_OBJECT=DSGFileMan789
DOCUMENT_ROOT=doc-root/
USER_FILE=
test-accounts.txt
The first three properties are used for binding the
IFileManager
to the RMI registry.
The property
DOCUMENT_ROOT
specifies the document root of
IFileManager
.
The last property
USER_FILE
defines the name of the file where the user accounts
are stored on the server. The keys of the property files can be found in the
IPropertyKeys interface.
You can assume that these properties you get in your constructor are
correct, so you do not have to check them. The USER_FILE
property
represents a filename which you *have* to load from the CLASSPATH. Do not load
it as a normal file with FileReader
or FileInputStream
.
For loading a file from the classpath, check out
java.lang.ClassLoader.
Printing output to System.out and
System.err
In this assignment, we use JUnit again to automatically test
your application. You are free to print any debug messages you
like to System.out or System.err, but
you will not see your debug output produced during
the execution of the grading robot in your grading mail (this
is because your debug output would probably cause confusion
and would not help as there might be dependencies between our
tests).
System.exit
In this assignment, you should not use System.exit
in your code. This might
cause unexpected problems during the automated testing. You should use
return
instead.
Testing issues
Note that when testing your solution, we try to start an instance
of your server and lookup the file manager from the registry.
Therefore, it does not make sense to use our grading robot before
your server can start up, and bind the IFileManager
to the registry, because all your tests will fail.
How to compile and run your code
As in Lab3, we provide a simple Ant file to compile your clients
(download here).
We assume that you have the following directory structure in your $HOME/lab4
directory:
dslab150@pizza:~/lab4$ ll
total 24
drwx--x--x 3 dslab150 dslab150 4096 2006-11-14 10:01 build
-rw------- 1 dslab150 dslab150 620 2006-11-14 18:17 build.xml
-rw------- 1 dslab150 dslab150 130 2006-11-14 14:03 DSGFileMan.properties
drwx--x--x 2 dslab150 dslab150 4096 2006-11-14 17:25 lib
drwx--x--x 3 dslab150 dslab150 4096 2006-11-03 17:31 src
-rw------- 1 dslab150 dslab150 178 2006-11-14 09:54 test-accounts.txt
Simply store the Ant script in a file called
build.xml
in
$HOME/lab4
and execute it
by issuing the command
ant compile
on the bash. Delete the compiled classes by issuing the command
ant clean
.
Your compiled Java classes will be stored in the
build
directory.
In contrast to Lab3, we do not provide a script for starting the server.
To start your implementation, simply enter the following command:
dslab150@pizza:~/lab4$ java -cp build/:.:`echo lib/*.jar | tr ' ' ':'`
-Djava.rmi.server.hostname=pizza.dslab.tuwien.ac.at
-Djava.rmi.server.codebase=http://www.dslab.tuwien.ac.at/lab4/DSGFileMan-lab4.jar
at.ac.tuwien.dslab.rmi.server.FileManServerImpl DSGFileMan.properties
Please note that the JVM property
-Djava.rmi.server.hostname=pizza.dslab.tuwien.ac.at
has to be set to the hostname where the server should be bound.
Furthermore, the property
-Djava.rmi.server.codebase=http://www.dslab.tuwien.ac.at/lab4/DSGFileMan-lab4.jar
has to be set to the jar-file that contains the remote interface.
Hints for Solving the Lab
-
If you are new to Java RMI programming, please check out
the reading suggestions below to get familiar with RMI.
-
The interface
IRepository
is only a suggestion by us.
Since this interface does not appear in the IFileManager
interface,
you can implement the repository functionality as you prefer.
-
In your home directory, you find the file
test-accounts.txt
which contains test users for the DSGFileMan system. Note that you have to put this
file to your CLASSPATH so that your server is able to read the user accounts.
Moreover, add your own dsgfm account to this file, if you want to test your server
with your account.
-
There are two RMI registries which you can use to bind your server. These registries are running on
rmi://pizza.dslab.tuwien.ac.at:1099
and rmi://pasta.dslab.tuwien.ac.at:1099
, respectively.
To avoid confusion with other students, use a unique name for your server
(for instance, user dslab789
should use DSGFileMan789
).
Moreover, you can also start your own registry using the command rmiregistry <port number>
.
To avoid confusion with other students, use a unique portname for your registry.
We suggest that user dslabXXX
uses port number 40+XXX
(for instance, user dslab789
uses port number 40789
).
-
If our grading robot tells you that your submission does not pass all the tests,
always start to correct the first test that fails because the other errors
could be consecutive failures.
Deliverables
To submit your solution, you need to follow these steps:
- Develop your solution in Java (version 1.5), and implement the
IFileManager
and the IFileManServer
interface as described above. Put all your files in the
$HOME/lab4/src/
folder and use the provided package
name (at.ac.tuwien.dslab.rmi.server
).
Make sure that your Java program compiles and runs in the
lab environment. You can implement and use as much classes as
you need. The only constraint is that your implement the
required interfaces and use the package name
at.ac.tuwien.dslab.rmi.server
for all you
implementation classes.
It is not allowed to use external libraries (e.g., XML
parser), they won't be submitted to the server.
- If you have created your files on some other machine, copy them into the lab environment using
scp
(secure copy).
- In your
$HOME/lab4/src
directory call submit4 to submit all
your files.
- Read any error or success messages.
- It might take up to an hour for the grading robot to check your
program. Don't forget to read your e-mail to check the results
of the grading robot. If you have errors, correct them and try again!
Reading Suggestions
Deadline
You need to submit your solution until December the 14th, 17:59.
Closing words
Have fun, without sockets ;-)
Last Modified: Die Jän 2 14:41:31 CET 2007