# [SVNKit] Commit sehr langsam.



## Tschokko (20. Sep 2007)

Hallo, 

normalerweise konnte mir das Internet bisher immer ausreichend helfen, doch diesmal finde ich nichts was mein Problem löst.

Kurz gefasst, ich brauch für eine Java Web Anwendung ein Dateianhänge Modul mit Versionierung. Habe mich mal für den SVN Server und als API das SVNKit entschieden. Seit gestern spiele ich ein wenig mit dem SVNKit rum und finde das bisher echt prima. Aaaaaaaaber wenn ich Dateien mit 5, 10 oder mehr MB über meine Testfunktionen einchecke ist das tierisch langsam. Eine 10 MB Datei dauert locker 8 Min !!! Das ist nicht akzeptabel.

Tja, was mach ich nun falsch? Ich gehe wirklich nach denen Anleitung des SVNKits vor und finde auch sonst keine anderen Lösungansätze. Das einzige wäre, wenn die Datei lokal auf dem Datenträger liegt, dann gibts noch weitere APIs. Aber ich möchte mit Stremas arbeiten.

Schaut doch mal drüber. In der Funktion addAttachment bleibt er genau beim Aufruf von sendDelta Minuten lang stehen, bzw. der Java Prozess ist emsig am Arbeiten, laut Task-Manager.


```
package test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;

public class Main {
	
	public Main() {
		setupAttachmentsModule();
	}

	private void setupAttachmentsModule() {
		DAVRepositoryFactory.setup();
		FSRepositoryFactory.setup();
	}
	
	private SVNRepository connectToRepository(String host, String repositoryName) throws SVNException {
		SVNURL url = SVNURL.parseURIEncoded("file://" + host + "/" + repositoryName);
		SVNRepository repository = SVNRepositoryFactory.create(url);
		return repository;		
	}
	
	private void createBaseDirectory(SVNRepository repository, Long id) throws SVNException {
		ISVNEditor editor = repository.getCommitEditor("Added new base directory for project '" + id + "'" , null);
		editor.openRoot(-1); 
		editor.addDir(id.toString(), null, -1);
		editor.closeDir(); // Close new base directory
		editor.closeDir(); // Close root directory
		editor.closeEdit();
	}
	
	private void addAttachment(SVNRepository repository, Long id, String fileName, byte[] fileData) throws SVNException {
		ISVNEditor editor = repository.getCommitEditor("Added new base directory for project '" + id + "'" , null, true, null);
		editor.openRoot(-1); 
		editor.openDir(id.toString(), -1);

		String filePath = id.toString() + "/" + fileName;
		editor.addFile(filePath, null, -1);
		editor.changeFileProperty(filePath, "dlvvvs:display-version", "1.5");
		
		editor.applyTextDelta(filePath, null);
		SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
		String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(fileData), editor, true);
		editor.closeFile(filePath, checksum);

		editor.closeDir(); // Close new base directory
		editor.closeDir(); // Close root directory
		
		
		
		editor.closeEdit();
		
	}

	private void addAttachment(SVNRepository repository, Long id, String fileName, InputStream fileStream) throws SVNException {
		ISVNEditor editor = repository.getCommitEditor("Added new base directory for project '" + id + "'" , null, true, null);
		editor.openRoot(-1); 
		editor.openDir(id.toString(), -1);

		String filePath = id.toString() + "/" + fileName;
		editor.addFile(filePath, null, -1);
		editor.changeFileProperty(filePath, "dlvvvs:display-version", "1.5");
		editor.changeFileProperty(filePath, SVNProperty.MIME_TYPE, SVNFileUtil.BINARY_MIME_TYPE);
		
		editor.applyTextDelta(filePath, null);
		SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
		String checksum = deltaGenerator.sendDelta(filePath, fileStream, editor, true);
		editor.closeFile(filePath, checksum);
		
		editor.closeDir(); // Close new base directory
		editor.closeDir(); // Close root directory
		
		
		
		editor.closeEdit();
		
	}
	
	private void updateAttachment(SVNRepository repository, Long id, String fileName, byte[] fileData, boolean incrementMajorVersion) throws SVNException {
		String filePath = id.toString() + "/" + fileName;

		// Get old file content
		Map properties = new HashMap();
		ByteArrayOutputStream oldFileData = new ByteArrayOutputStream();
		repository.getFile(filePath, -1, properties, oldFileData);
		
		ISVNEditor editor = repository.getCommitEditor("Added new base directory for project '" + id + "'" , null);
		editor.openRoot(-1); 
		editor.openDir(id.toString(), -1);
		
		editor.openFile(filePath, -1);
		
		editor.applyTextDelta(filePath , null);
		SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
		String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(oldFileData.toByteArray()), 0 , new ByteArrayInputStream(fileData), editor, true);
		
		String displayVersion = (String)properties.get("dlvvvs:display-version");
		if (displayVersion == null || displayVersion.trim().equals("") == true)
			displayVersion = "1.0";
		
		String displayVersionInfo[] = displayVersion.split("\\.");
		int majorVersion = 1, minorVersion = 0;
		if (displayVersionInfo.length >= 1) 
			majorVersion = Integer.parseInt(displayVersionInfo[0]);
		if (displayVersionInfo.length >= 2) 
			minorVersion = Integer.parseInt(displayVersionInfo[1]);
		
		// increment display-version
		if (incrementMajorVersion) {
			majorVersion++;
			minorVersion = 0;
		}
		else
			minorVersion++;
		
		displayVersion = new Integer(majorVersion).toString() + "." + new Integer(minorVersion).toString();
		editor.changeFileProperty(filePath, "dlvvvs:display-version", displayVersion);
		
		editor.closeFile(filePath, checksum);

		editor.closeDir(); // Close new base directory
		editor.closeDir(); // Close root directory
		
		editor.closeEdit();
	}
	
	private List<String> getAttachmentList(SVNRepository repository, Long id) throws SVNException {
		List<String> attachments = new ArrayList<String>();
		SVNNodeKind nodeKind = repository.checkPath(id.toString(), -1);
		if (nodeKind == SVNNodeKind.DIR) {
			Map properties = new HashMap();
			Collection entries = repository.getDir(id.toString(), -1 , null, (Collection) null);
			
			//System.out.println(properties.toString());
			Iterator iterator = entries.iterator( );
			while (iterator.hasNext()) {
				SVNDirEntry entry = (SVNDirEntry) iterator.next();
				System.out.println( entry.getName() + " (author: '" + entry.getAuthor() + "'; revision: " + entry.getRevision() + "; date: " + entry.getDate() + ")");
				if (entry.getKind() == SVNNodeKind.FILE) {
					repository.getFile(id.toString() + "/" + entry.getRelativePath(), -1, properties, null);
					System.out.println(properties.toString());
					attachments.add(entry.getName());
				}
				
			}
		}
		return attachments;
	}
	
	public static void main(String[] args) {
		byte[] contents = "This is the new text3 file.".getBytes();
		Long agreementId = new Long(20091978);
		
		try {
			Main app = new Main();
			SVNRepository repository = app.connectToRepository("/C:/AttachmentsArchive", "repo");
			//app.createBaseDirectory(repository, agreementId);
			
			/*FileInputStream fis = new FileInputStream("C:/Temp/blackbox_3.jpg");
			FileChannel fc = fis.getChannel();
			byte[] data = new byte[(int)fc.size()]; 
			ByteBuffer bb = ByteBuffer.wrap(data);
			fc.read(bb);*/
			FileInputStream newFile = new FileInputStream("C:/Temp/819-4284-10.pdf");
			
			app.addAttachment(repository, agreementId, "test9.txt", newFile);
			//app.updateAttachment(repository, agreementId, "test1.txt", newFile, true);
			List<String> attachments = app.getAttachmentList(repository, agreementId);
			
			attachments.size();
		}
		catch (SVNException e) {
			e.printStackTrace();
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
	}

}
```


Ach ja, wenn ich mit Subclipse (baut auf SVNKit auf !) oder mit TortoiseSVN arbeite geht das Einchecken der großen Datein ruck zuck in Sekundenbereichen. 

Vielen Dank schon mal für eure Hilfe.

Gruß
Tschokko


----------



## Murray (20. Sep 2007)

Du hast ja zwei addAttachment-Methoden, eine mit einem Byte-Array und eine mit einem Stream. Falls du direkt die Methode mit dem Stream aufrufst: was für einen Stream übergibst du dann?


----------



## Tschokko (20. Sep 2007)

Hallo,

schau mal in die main Funtktion.  Dort sollte sichtbar sein, welchen Stream ich übergeben, als auch welches byte[] ich zuvor übergeben.

Gruß Tschokko


----------



## Murray (20. Sep 2007)

Versuch doch mal, die Datei vorher in ein Byte-Array zu lesen und die andere Methode zu verwenden, um die Zeiten für den Dateizugriff und für den Vergelich auseinanderhalten zu können. Wenn das auch nicht schneller geht, dann wird man wohl von außen ewig machen können, denn dann dauert es wohl wirklich so lange, die Differenzen zu bestimmen.

Baisert Subclipse wirklich auf SVNKit? Soweit ich weiss, enthält Subclipse doch eine native Implementierung für den SVN-Zugriff, während SVNKit Java-only ist.


----------



## Tschokko (21. Sep 2007)

Murray hat gesagt.:
			
		

> Baisert Subclipse wirklich auf SVNKit? Soweit ich weiss, enthält Subclipse doch eine native Implementierung für den SVN-Zugriff, während SVNKit Java-only ist.


Auf den Seiten von SVNKit wird Subclipse als Projekt das SVNKit nutzt aufgelistet.


Ich hab zuvor die Methode mit dem Byte-Array Parameter genutzt. War genauso langsam. Wie ich das Byte Array gefüllt habe, sieht man in dem kommentierten Block in main. 

Gibt es Alternativen zu SVNKit ?

Gruß


----------



## kama (21. Sep 2007)

Hallo,



> Baisert Subclipse wirklich auf SVNKit? Soweit ich weiss, enthält Subclipse doch eine native Implementierung für den SVN-Zugriff, während SVNKit Java-only ist.



Es ist so, dass Subclipse die JavaHL Bindings von Subversion nutzt, sprich via JNI direct die C-Bibliotheken aufruft. Man kann alternativ auch SVNKit verwenden. Man kann aber auch Subversive verwenden und dann ist der typische Anwendungsfall eben SVNKit....damit spart man sich nämlich die Installation von Subversion auf dem jeweiligen Client.

MfG
Karl Heinz Marbaise


----------



## Murray (21. Sep 2007)

kama hat gesagt.:
			
		

> Es ist so, dass Subclipse die JavaHL Bindings von Subversion nutzt, sprich via JNI direct die C-Bibliotheken aufruft. Man kann alternativ auch SVNKit verwenden.


Wobei auf den Subclipse-Seiten zu lesen ist, dass es mit SVNKit sogar etwas schneller sein soll als mit den JavaHL-Bindings.

Ich bin mit SVN nicht so vertraut, aber ist es nicht so, dass die hier verwendete Operation die Unterschiede zwischen zwei Dateien ermittelt und nur diese Unterschiede committed, was primär bei Textdateien sinnvoll ist? Große Binärdateien sollte man ja besser komplett ersetzen.


----------



## kama (21. Okt 2007)

Hallo,
sorry hat was gedauert...



			
				Murray hat gesagt.:
			
		

> Ich bin mit SVN nicht so vertraut, aber ist es nicht so, dass die hier verwendete Operation die Unterschiede zwischen zwei Dateien ermittelt und nur diese Unterschiede committed, was primär bei Textdateien sinnvoll ist? Große Binärdateien sollte man ja besser komplett ersetzen.


Es werden die Unterschiede sowohl für Text als auch für Binär Dateien ermittelt und das sehr effizient.
Deinen letzten Satz kann ich so nicht unterschreiben, da auch Nutzung in Subversion von großen Dokumenten (Word, Excel, Grafiken etc.) sehr gut klappt. Abgesehen, ist das ersetzten einer Datei nicht wirklich die Alternative, da das dann mehr Speicherplatz benötigt als nur den Unterschied zu speichern.


MfG
Karl Heinz Marbaise


----------

