CVSRoot
"Ojares Rami EINT" <[email protected]> Fri, 12 Sep 2003 13:43:54 +0300
| Newsgroups | gmane.comp.java.netbeans.modules.javacvs.devel |
|---|---|
| Message-ID | <[email protected]> |
I wrote for my self a new CVSRoot class because at the time it was faster for me.
Now I would like to discuss a few thoughts before submitting obscure patches.
First the distinction between local and server connection types.
Now both use ServerConnection that does not use sockets but both
execute cvs server on local machine.
Should it be so that there are two different classes
ServerConnection: opens socket to host and starts cvs server
LocalConnection: starts cvs server on local machine.
There are few aspects to CVSRoot
1. recognizing existing cvsroots correctly
2. writing new cvsroots correctly
3. comparing cvsroots for equality
Let's review LOCAL connection type
It should recognize urls with format
[:local:[username:][password:]]repositoryPath
So if :local: does not exist username and password are not allowed.
Since local connection never uses username or password
it should always create urls of format
:local:repositoryPath
Of course it could also create url
repositoryPath
but it is maybe less clear because it omits the connection type.
And it would be more clear if it would tell the user what kind of
cvsroots it creates.
Then equality should only compare the equality of repository
for LOCAL connection type.
Here is an implementation of CVSROOT that tries to be clear of those
issues and after it ConnectionFactory that implements the distinction between
LOCAL and SERVER.
public class CVSRoot {
public static final int LOCAL = 1;
public static final int SERVER = 2;
public static final int PSERVER = 3;
// covers only ssh2 protocol
public static final int EXT = 4;
// the official javacvs representation of the cvsroot
String cvsroot;
// One of the constants intoroduced by this class
int connectionType;
// user (default = null)
String userName;
// password (default = null)
String password;
// host (default = null)
String host;
// port (default = 0)
int port;
// repository as string representation
String repository;
/**
This constructor allows to construct CVSRoot from Properties object.
The names are exactly the same as the attribute names in this class
*/
public Root(Properties props) throws IllegalArgumentException {
String ct = props.getProperty("connectionType");
if (ct == null)
connectionType = LOCAL;
else if (ct.equalsIgnoreCase("local"))
connectionType = LOCAL;
else if (ct.equalsIgnoreCase("server"))
connectionType = SERVER;
else if (ct.equalsIgnoreCase("pserver"))
connectionType = PSERVER;
else if (ct.equalsIgnoreCase("ext"))
connectionType = EXT;
else
throw new IllegalArgumentException("Connection type " + ct + " not supported.");
// user & password (may be null too)
this.username = props.getProperty("username");
this.password = props.getProperty("password");
// host & port
this.host = props.getProperty("host");
if (this.host == null && this.connectionType != LOCAL)
throw new BuildException("host is obligatory in non local connections.");
try {
int p = Integer.parseInt(props.getProperty("port"));
if (p > 0)
this.port = p;
}
catch (Exception e) {
// never mind
}
// and the most important which is repository
String r = props.getProperty("repository");
if (r == null)
throw new IllegalArgumentException("Repository is obligatory.");
else
this.repository = r;
// construct string representation of cvsroot
if (this.connectionType == LOCAL) {
this.cvsroot = ":local:" + repository;
}
if (this.connectionType == SERVER) {
// never put password in cvsroot
String str = ":server:" + this.username + "@" + this.host + ":";
if (this.port > 0)
str += "" + this.port;
str += this.repository;
this.cvsroot = str;
}
if (this.connectionType == PSERVER) {
// never put password in cvsroot
String str = ":pserver:" + this.username + "@" + this.host + ":";
if (this.port > 0)
str += "" + this.port;
str += this.repository;
this.cvsroot = str;
}
if (this.connectionType == EXT) {
// never put password in cvsroot
String str = ":ext:" + this.username + "@" + this.host + ":";
if (this.port > 0)
str += "" + this.port;
str += this.repository;
this.cvsroot = str;
}
}
public Root(String cvsroot) throws IllegalArgumentException {
this.cvsroot = cvsroot;
if (cvsroot.startsWith(":local:")) {
this.connectionType = LOCAL;
this.repository = cvsroot.substring(7);
}
else if (!cvsroot.startsWith(":")) {
// if the root does not start with : it means that it is just a local path
this.connectionType = LOCAL;
this.repository = cvsroot;
}
else if (
cvsroot.startsWith(":server:") ||
cvsroot.startsWith(":pserver:") ||
cvsroot.startsWith(":ext:")
) {
// format:
// :(server|pserver|ext):username[:password]@host:[port]repository
// check that @ exists
int at = cvsroot.indexOf("@");
if (at == -1)
throw new IllegalArgumentException(this.cvsroot + " must contain @ sign.");
// left from @ sign
String leftSide;
// Find next colon to right from @ sign
rightColon = cvsroot.indexOf(':', at+1);
if (rightColon == -1)
throw new IllegalArgumentException("Repository not found in cvsroot " + cvsroot);
if (cvsroot.startsWith(":server:")) {
this.connectionType = SERVER;
leftSide = cvsroot.substring(8, at);
}
else if (cvsroot.startsWith(":pserver:")) {
this.connectionType = PSERVER;
leftSide = cvsroot.substring(9, at);
}
else if (cvsroot.startsWith(":ext:")) {
this.connectionType = EXT;
leftSide = cvsroot.substring(5, at);
}
int colon = leftSide.indexOf(":");
if (colon != -1) {
this.username = leftSide.substring(0, colon);
this.password = leftSide.substring(colon+1);
}
else {
this.username = leftSide;
}
this.host = cvsroot.substring(at+1, rightColon);
// pr = [port]repository
String pr = cvsroot.substring(rightColon+1);
Matcher matcher = Pattern.compile("\\d+").matcher(pr);
if (matcher.lookingAt()) {
try {
this.port = Integer.parseInt(matcher.group());
} catch(NumberFormatException e) {}
this.repository = pr.substring(matcher.end());
}
else {
this.repository = pr;
}
}
else {
// we were unable to recognize the connection type
throw new BuildException("Unable to recognize cvsroot " + cvsroot);
}
}
public String toString() {
return this.cvsroot;
}
public boolean equals(Object o) {
Root compared;
try {
compared = (Root) o;
}
catch(ClassCastException cce) {
return false;
}
// connection comparison
if (this.connectionType == compared.connectionType) {
if (this.connectionType == LOCAL) {
// repositories are first wrapped inside File and then those files are compared
if (
(new File(this.repository)).equals(
new File(compared.repository)
)
)
return true;
else
return false;
}
else {
// host is compared case insensitively
if (
this.host.equalsIgnoreCase(compared.host)
&&
this.port == compared.port
&&
(new File(this.repository)).equals(
new File(compared.repository)
)
)
return true;
else
return false;
}
}
else
/*
if connection type is not equal then we return false.
but it could be possible to only compare host and repository because you could connect to the repository using multiple methods.
But of course this would screw up the cvsroot representation witten into administrative files.
*/
return false;
}
// at the moment no setters, because they have effect on cvsroot
// need to think whether this should be modified in code after creation.
public String getCvsroot() {
return cvsroot;
}
public int getConnectionType() {
return connectionType;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getRepository() {
return repository;
}
}
And then the ConnectionFactory's getConnection method
// Note the parameter is CVSRoot instead of string (allows us to create CVSRoot the way we want.
public static Connection getConnection(CVSRoot root) throws IllegalArgumentException {
if (root.getConnectionType() == CVSRoot.LOCAL) {
return new LocalConnection(root);
}
if (root.getConnectionType() == CVSRoot.SERVER) {
return new ServerConnection(root);
}
if (root.getConnectionType() == CVSRoot.PSERVER) {
return new PServerConnection(root);
}
if (root.getConnectionType() == CVSRoot.EXT) {
return new SSHConnection(root);
}
throw new IllegalArgumentException("Unrecognized CvsRoot: " + cvsRoot);
}
The PServerConnection has to be changed a bit to accommodate the changes in CVSRoot
but that is easy.
I can change a diff with the changes + SSHConnection addition.
J2SSH library requires commons-logging package so that would have to be included
too (which is not so nice). But maybe javacvs could use that logging package too
instead of obscure System properties.
- rami