Saturday, September 8, 2012

Creating SVN Repository Remotely

Linux distribution of Apache subversion provides svnadmin to create a svn repository.But this is not capable of creating repository remotely.By issuing svnadmin command we can only create a repository within local file system.
Here i am going to describe couple of ways by which we can create svn repositories remotely.Here all my ways achieve above mentioned objective through  java.Here is the list of method that i found.

1.Using ssh to execute svnadmin command remotely.
    a)Using apache sshd library
    b)Using JSch library
2.Using SCM -Manager
3.Using PHP+java Client

Using ssh to execute svnadmin command remotely

In this approach the repository is created by executing svnadmin or relevant command in remote host using ssh.Here there are two ssh opensource libraries available for java.

a) Using apache sshd library

apache sshd is a pure java implementation of ssh available under apache 2.0 license.This is a sub-project of apache Mina.Here is a example code for executing svnadmin command on remote host as a superuser.

package org.wso2.carbon;
import org.apache.sshd.ClientChannel;
import org.apache.sshd.ClientSession;
import org.apache.sshd.SshClient;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
*
*/
public class SSHDClient {
public static void main(String[] args) {
SshClient client = SshClient.setUpDefaultClient();
try {
client.start();
ClientSession clientSession = client.connect("localhost", 22).await().getSession();
clientSession.authPassword("root", "password");
ClientChannel channel = clientSession.createChannel("exec", "sudo -S -p '' -u www-data svnadmin create /home/root/repository/af5 \n");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Writer w = new OutputStreamWriter(baos);
w.append("password");
w.append("\n");
w.close();
channel.setIn(new ByteArrayInputStream(baos.toByteArray()));
channel.setOut(System.out);
channel.setErr(System.err);
channel.open();
channel.waitFor(ClientChannel.CLOSED, 0);
channel.close(false);
client.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
view raw gistfile1.java hosted with ❤ by GitHub
To use apache sshd library you have to add following maven dependency. 
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.7.0</version>
</dependency>
view raw gistfile1.xml hosted with ❤ by GitHub


b)Using JSch library

This is another popular java implementation of ssh.There are  good examples also available for this libarary.Here is how to execute svnadmin command using JSch library.
package org.wso2.carbon;
import com.jcraft.jsch.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class SSHClient {
public static void main(String[] args) throws JSchException, IOException {
String host = "localhost"; // host IP
String user = "root"; // username for SSH connection
String password = "password"; // password for SSH connection
int port = 22; // default SSH port
JSch shell = new JSch();
// get a new session
Session session = shell.getSession(user, host, port);
// set user password and connect to a channel
session.setUserInfo(new SSHUserInfo(password));
session.connect();
Channel channel = session.openChannel("exec");
ChannelExec channelExec= (ChannelExec) channel;
channelExec.setCommand("sudo -S -p '' -u www-data "+"svnadmin create /home/root/repository/af191 \r\n");
channelExec.connect();
DataInputStream dataIn = new DataInputStream(channelExec.getInputStream());
DataOutputStream dataOut = new DataOutputStream(channelExec.getOutputStream());
dataOut.write("password\n".getBytes());
dataOut.flush();
System.out.println(channel.getExitStatus());
dataIn.close();
dataOut.close();
channelExec.disconnect();
session.disconnect();
return;
}
// this class implements jsch UserInfo interface for passing password to the session
static class SSHUserInfo implements UserInfo {
private String password;
SSHUserInfo(String password) {
this.password = password;
}
public String getPassphrase() {
return null;
}
public String getPassword() {
return password;
}
public boolean promptPassword(String arg0) {
return true;
}
public boolean promptPassphrase(String arg0) {
return true;
}
public boolean promptYesNo(String arg0) {
return true;
}
public void showMessage(String arg0) {
System.out.println(arg0);
}
}
}
view raw gistfile1.java hosted with ❤ by GitHub
Following is the maven dependency which can be used to create maven project.

<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.44-1</version>
</dependency>
view raw gistfile1.xml hosted with ❤ by GitHub
Using SCM -Manager

Next approach is using a source code management system.SCM Manager is an open source source code management system which is implemented in java.This is
a server which provide user interface to create repository and manage them with users.Currently it support svn ,git and mercurial.In addition to user interface, it expose REST API to manage repositories.Here i am going to use the REST API to create a repository.Before staring writing the REST Client Download the SCM Manager from here and start the server by executing the script which can be found in bin folder of the distribution.
To write the REST client there so many ways.
  1.Using HTTP client
  2.Apache Wink client
  3.Jersey Client
I am using http client wich is very simple to use.All the REST calls are protected by BASIC Auth in SCM  Manager.We have to set the credential in http client.
package org.wso2.carbon;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.StringWriter;
/**
*
*
*/
public class SVNClient {
private static final String REST_BASE_URI = "/api/rest";
private static final String REST_CREATE_REPOSITORY_URI = "/repositories";
private static final String REST_GET_REPOSITORY_URI = "/repositories/svn/";
//repositories xml elements
private static final String REPOSITORY_XML_ROOT_ELEMENT = "repositories";
private static final String REPOSITORY_NAME_ELEMENT = "name";
private static final String REPOSITORY_TYPE_ELEMENT = "type";
private static final String REPOSITORY_URL_ELEMENT = "url";
//permission xml elements
private static final String PERMISSION_XML_ROOT_ELEMENT = "permissions";
private static final String PERMISSION_TYPE_ELEMENT = "type";
private static final String PERMISSION_NAME_ELEMENT = "name";
private static final String PERMISSION_GROUP_PERMISSION_ELEMENT = "groupPermission";
public static void main(String[] args) {
HttpClient client = new HttpClient();
String userName = "scmadmin";
String password = "scmadmin";
AuthScope authScope = AuthScope.ANY;
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userName, password);
client.getState().setCredentials(authScope, credentials);
PostMethod post = new PostMethod("http://localhost:8080/" + REST_BASE_URI +
REST_CREATE_REPOSITORY_URI);
post.setRequestEntity(new ByteArrayRequestEntity(getRepositoryAsString("testRepo")));
post.setDoAuthentication(true);
post.addRequestHeader("Content-Type", "application/xml;charset=UTF-8");
try {
client.executeMethod(post);
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
HttpConnectionManager manager = client.getHttpConnectionManager();
if (manager instanceof SimpleHttpConnectionManager) {
((SimpleHttpConnectionManager) manager).shutdown();
}
}
}
private static byte[] getRepositoryAsString(String appid) {
OMFactory factory = OMAbstractFactory.getOMFactory();
OMElement repository = factory.createOMElement(REPOSITORY_XML_ROOT_ELEMENT, null);
OMElement name = factory.createOMElement(REPOSITORY_NAME_ELEMENT, null);
name.setText(appid);
repository.addChild(name);
OMElement type = factory.createOMElement(REPOSITORY_TYPE_ELEMENT, null);
type.setText("svn");
repository.addChild(type);
OMElement permission = factory.createOMElement(PERMISSION_XML_ROOT_ELEMENT, null);
OMElement groupPermission = factory.createOMElement(PERMISSION_GROUP_PERMISSION_ELEMENT, null);
groupPermission.setText("false");
permission.addChild(groupPermission);
OMElement permName = factory.createOMElement(PERMISSION_NAME_ELEMENT, null);
permName.setText("RW");
OMElement permType = factory.createOMElement(PERMISSION_TYPE_ELEMENT, null);
permType.setText("OWNER");
permission.addChild(permName);
permission.addChild(permType);
repository.addChild(permission);
StringWriter writer = new StringWriter();
try {
repository.serialize(writer);
} catch (XMLStreamException e) {
e.printStackTrace();
} finally {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return writer.toString().getBytes();
}
}
view raw gistfile1.java hosted with ❤ by GitHub
Here i used Axiom for generating XML payload.

Using PHP+java Client

In this approach a php page that is hosted in apache 2 server in remote host will create a svn repository using svnadmin command and there will be a java client that is used to call the php.The php page is secured using Basic Auth.Here is the very simple php code to create svn repository.

<?php
//There should be a proper way of handling authentication.
//This is just for POC
$ADMIN_USER_NAME='admin';
$ADMIN_PASSWORD='admin';
$PARENT_REPO_PATH='/home/root/repository/';
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo '401 Authorization Required';
exit;
} else {
if((strcmp($_SERVER['PHP_AUTH_USER'],$ADMIN_USER_NAME)==0) && (strcmp($_SERVER['PHP_AUTH_PW'],$ADMIN_PASSWORD)==0)){
$name = $_REQUEST['name'];
$cmd = 'svnadmin create ' . escapeshellarg($PARENT_REPO_PATH . $name) . ' --fs-type fsfs';
$output1=exec($cmd, $output, $exitValue);
if ($exitValue != 0) {
Print $output1;
header('HTTP/1.1 500 Internal Server Error');
return;
}else{
header('HTTP/1.1 201 Created');
return;
}
}else{
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo '401 Authorization Required';
exit;
}
}
?>
view raw gistfile1.php hosted with ❤ by GitHub
Here is the java client that is used to call the php page.
package org.wso2.carbon;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import java.io.IOException;
/**
*
*/
public class PHPBasedSVNRepositoryClient {
public static final String CREATE_REPO_EPR = "/createRepo.php";
public static void main(String[] args) {
HttpClient client = new HttpClient();
String userName = "admin";
String password = "admin";
AuthScope authScope = AuthScope.ANY;
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userName, password);
client.getState().setCredentials(authScope, credentials);
;
GetMethod get = new GetMethod("http://localhost" + CREATE_REPO_EPR);
NameValuePair[] repositoryName = new NameValuePair[1];
repositoryName[0] = new NameValuePair();
repositoryName[0].setName("name");
repositoryName[0].setValue("testRepo");
get.setQueryString(repositoryName);
get.setDoAuthentication(true);
try {
client.executeMethod(get);
if (get.getStatusCode() == HttpStatus.SC_CREATED) {
System.out.println("repository is created successfully");
} else {
System.out.println("repository creation is failed");
}
} catch (IOException e) {
System.out.println("repository creation is failed " + e.getLocalizedMessage());
} finally {
get.releaseConnection();
}
}
}
view raw gistfile1.php hosted with ❤ by GitHub
 
This is how we can create svn repositories remotely.This will be helpful when ever there is a requirement of automatic repository provisioning.