Getting Started with JavaCVS
Introduction
This short document helps you get started with JavaCVS. There is a
design overview document that goes into
more details about the library's classes but you should not need to know
the details to use the library.
Prerequisites are:
- Know how to program in Java (obviously)
- Have used CVS before, know what the commands do etc. You do not
need to understand the protocol or any details of the cvs implementation,
but anything you know will make things easier for you.
Details
Getting the CVSROOT
The first thing you need to do is set up some global options for whichever
command(s) you want to run. The most important thing is obviously the
setting for CVSROOT.
You can do at least two things:
- Check if you have a CVS directory in the current working directory
and read the value from the Root file within it.
- Read a system property
Here is some code to help you out with both of these tasks:
String root = null;
BufferedReader r = null;
try
{
File f = new File(System.getProperty("user.dir"));
File rootFile = new File(f, "CVS/Root");
if (rootFile.exists())
{
r = new BufferedReader(new FileReader(rootFile));
root = r.readLine();
}
}
catch (IOException e)
{
// ignore
}
finally
{
try
{
if (r != null)
r.close();
}
catch (IOException e)
{
System.err.println("Warning: could not close CVS/Root file!");
}
}
if (root==null)
{
root = System.getProperty("cvs.root");
}
Having got some value for CVSROOT you should create a
GlobalOptions
object and set the root on it:
GlobalOptions globalOptions = new GlobalOptions();
globalOptions.setCVSRoot(getCVSRoot());
Your application will probably want to make use of the various fields within
the CVSROOT in order to connect and so on. You can do that any way you want but
this class is a handy way of parsing CVSROOT:
/**
* A struct containing the various bits of information in a CVS root
* string, allowing easy retrieval of individual items of information
*/
private static class CVSRoot
{
public String connectionType;
public String user;
public String host;
public String repository;
public CVSRoot(String root) throws IllegalArgumentException
{
if (!root.startsWith(":"))
throw new IllegalArgumentException();
int oldColonPosition = 0;
int colonPosition = root.indexOf(':', 1);
if (colonPosition==-1)
throw new IllegalArgumentException();
connectionType = root.substring(oldColonPosition + 1, colonPosition);
oldColonPosition = colonPosition;
colonPosition = root.indexOf('@', colonPosition+1);
if (colonPosition==-1)
throw new IllegalArgumentException();
user = root.substring(oldColonPosition+1, colonPosition);
oldColonPosition = colonPosition;
colonPosition = root.indexOf(':', colonPosition+1);
if (colonPosition==-1)
throw new IllegalArgumentException();
host = root.substring(oldColonPosition+1, colonPosition);
repository = root.substring(colonPosition+1);
if (connectionType==null || user==null || host==null ||
repository==null)
throw new IllegalArgumentException();
}
}
From that you can get the connection type to determine what sort of connection
to the server to create. We will assume you are using pserver although other
connections are handled similarly.
Establishing a connection
We assume that you have obtained the password from somewhere (e.g. looking up
the .cvspass file). To connect:
PServerConnection c = new PServerConnection();
connection = c;
c.setUserName(userName);
c.setEncodedPassword(encodedPassword);
c.setHostName(hostName);
c.setRepository(repository);
c.open();
Creating a Client to execute commands
The
org.netbeans.lib.cvsclient.Client class is the central
controller for the library. To create one, you pass in a
Connection instance and an
AdminHandler.
An
AdminHandler is used to store and retrieve all the
administration files that CVS uses to know about which versions of files you
have checked out and so on. You don't have to worry about how it works and
a standard implementation is provided for you that works exactly like
command-line CVS. It creates a CVS subdirectory in all CVS directories and
stores its details in there.
Hence constructing a
StandardAdminHandler is all you need to do:
Client client = new Client(c, new StandardAdminHandler());
The client also needs to know the
local path. This is the directory
where the application is logically working from. This is easiest to understand
with command-line CVS - the localPath is just the directory you are in when
you are typing in your commands. From within a GUI application this is not
always so clear but basically any relative paths are constructed using
localPath as their root.
So set the local path to something appropriate:
client.setLocalPath(localPath);
Listening
You are almost ready to run a command. However, you need to register
at least one listener if you want to be able to do anything useful.
The library uses listeners to tell the application of any files that have
been updated, added, or removed and to communicate messages and progress with
the application.
Sophisticated GUI applications will want to handle all the events (see the
design overview document for more details)
however our simple client can just use the message events to find out what
has been going on.
The following class can be used as a simple listener.
CVSAdapter
is a class that implements the
CVSListener interface with empty
methods (cf.
java.awt.MouseAdapter).
public class BasicListener extends CVSAdapter
{
/**
* Stores a tagged line
*/
private final StringBuffer taggedLine = new StringBuffer();
/**
* Called when the server wants to send a message to be displayed to
* the user. The message is only for information purposes and clients
* can choose to ignore these messages if they wish.
* @param e the event
*/
public void messageSent(MessageEvent e)
{
String line = e.getMessage();
PrintStream stream = e.isError() ? System.err
: System.out;
if (e.isTagged())
{
String message = e.parseTaggedMessage(taggedLine);
// if we get back a non-null line, we have something
// to output. Otherwise, there is more to come and we
// should do nothing yet.
if (message != null)
{
stream.println(message);
}
}
else
{
stream.println(line);
}
}
}
At first glance, this might appear bizarre. The reason is that CVS can send
back several messages intended as one (so-called "tagged messages").
Fortunately, the library handles this for you. All you need to do is pass
in a StringBuffer to the
parseTaggedMessage method of the
MessageEvent class and if
enough data has been sent to make a decent message, you will be given back a
String message. Otherwise, the data is appended to the buffer.
Non-tagged messages are straightforward and can just be sent to an output
stream.
Having created an event listener, add it to the client's event manager:
client.getEventManager().addCVSListener(new BasicListener());
Running a command
Executing a command is simply a matter of instantiating an appropriate
Command class and pass it to the client to execute.
Many commands use a
Builder to generate useful data structures
containing information (such as the output from the Status command). For
our simple application we won't use a builder since we just output the
details straight to the console.
UpdateCommand command = new UpdateCommand();
command.setBuilder(null);
command.setRecursive(true);
command.setBuildDirectories(true);
command.setPruneDirectories(true);
client.executeCommand(command);
That executes the equivalent of
cvs update -dP.