Inital commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
|
||||||
|
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>SyncTool</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.maven.ide.eclipse.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.maven.ide.eclipse.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#Thu Mar 17 11:56:15 CET 2011
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=UTF-8
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#Thu Mar 17 11:56:15 CET 2011
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
line.separator=\n
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#Sat Feb 19 22:13:15 CET 2011
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.6
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#Fri Feb 18 19:10:50 CET 2011
|
||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
fullBuildGoals=process-test-resources
|
||||||
|
includeModules=false
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
resourceFilterGoals=process-resources resources\:testResources
|
||||||
|
skipCompilerPlugin=true
|
||||||
|
version=1
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
Weitere Optionen
|
||||||
|
- no sync for conflicting files
|
||||||
|
- exclude files (via regex)
|
||||||
|
|
||||||
|
|
||||||
|
Alternativer Ansatz:
|
||||||
|
Statt die Verzeichnisinhalte in Listen zu verwalten, könnte man die Liste beim Betreten eines Verzeichnisses
|
||||||
|
neu einlesen und sortieren. Wenn man "von unten" aus der Rekursion zurück kommt, macht man bei der Datei weiter,
|
||||||
|
die in der aktuellen Liste als nächstes kommt.
|
||||||
|
Vorteil: Geringerer Speicherbedarf, da keine Datei-Listen des gesamten Verzeichnis-Baumes oberhalb der aktuellen
|
||||||
|
Position gehalten werden müssen.
|
||||||
|
Nachteil: Ständiges Lesen und Sortieren der Verzeichnisinhalte.
|
||||||
|
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
-no-delete
|
||||||
|
-no-replace
|
||||||
|
-starttime
|
||||||
|
-logfile
|
||||||
|
-db -- path to database
|
||||||
|
-dry-run -- perform a trial run with no changes made
|
||||||
|
-hash -- compare the files by their MD5 hashes
|
||||||
|
-unidirectional/-oneway
|
||||||
|
-granularity <ms>
|
||||||
|
|
||||||
|
-times -- synchronize modification times (only)
|
||||||
|
-owner/-group -- synchronize owner/group
|
||||||
|
-version
|
||||||
|
-exclude/-include
|
||||||
|
-silent/-verbose
|
||||||
|
-nowriteprotection
|
||||||
|
-help
|
||||||
|
-mailto -- http://openbook.galileodesign.de/javainsel5/javainsel16_010.htm#Rxx747java160100400063D1F048118
|
||||||
|
-jabber -- http://www.igniterealtime.org/projects/smack/index.jsp
|
||||||
|
|
||||||
|
Apache Commons:
|
||||||
|
DirectoryWalker: evtl. für Abbruch
|
||||||
|
FileUtils: copy & delete
|
||||||
|
|
||||||
|
Tests: Dateien mit Mockito simulieren?
|
||||||
|
|
||||||
|
Dateien kopieren: http://openbook.galileocomputing.de/javainsel8/javainsel_14_003.htm#mjdbd1ea4c51ff569d905c698602549180
|
||||||
|
MD5 on the fly: http://stackoverflow.com/questions/304268/using-java-to-get-a-files-md5-checksum
|
||||||
|
MD5 direkt: String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(new FileInputStream(srcFiles[0]));
|
||||||
|
|
||||||
|
twalther42@jabber.org
|
||||||
|
dt1Ge3kEf
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>SyncTool</groupId>
|
||||||
|
<artifactId>synctool</artifactId>
|
||||||
|
<version>1.2.1</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>SyncTool</name>
|
||||||
|
<description>Directory Synchronization Tool</description>
|
||||||
|
<url>http://www.tilman.de/programme/synctool</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.martiansoftware</groupId>
|
||||||
|
<artifactId>jsap</artifactId>
|
||||||
|
<version>2.1</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>log4j</groupId>
|
||||||
|
<artifactId>log4j</artifactId>
|
||||||
|
<version>1.2.14</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<version>1.3.150</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.4</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.0.1</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jivesoftware</groupId>
|
||||||
|
<artifactId>smack</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jivesoftware</groupId>
|
||||||
|
<artifactId>smackx</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hsqldb</groupId>
|
||||||
|
<artifactId>hsqldb</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Tilman Walther
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.tilman.log4j;
|
||||||
|
|
||||||
|
import org.apache.log4j.AppenderSkeleton;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.log4j.PatternLayout;
|
||||||
|
import org.apache.log4j.spi.LoggingEvent;
|
||||||
|
import org.jivesoftware.smack.Chat;
|
||||||
|
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||||
|
import org.jivesoftware.smack.MessageListener;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple log4j appender that connects the logger to an XMPP recipient.
|
||||||
|
*
|
||||||
|
* @author Tilman Walther
|
||||||
|
*/
|
||||||
|
public class JabberAppender extends AppenderSkeleton {
|
||||||
|
|
||||||
|
private final static Logger log = Logger.getLogger(JabberAppender.class);
|
||||||
|
|
||||||
|
XMPPConnection connection;
|
||||||
|
String recipient;
|
||||||
|
Chat chat;
|
||||||
|
|
||||||
|
public JabberAppender(String recipient, String server, String user, String password) throws XMPPException {
|
||||||
|
this.setLayout(new PatternLayout("%m%n"));
|
||||||
|
|
||||||
|
log.info("Connecting to " + server);
|
||||||
|
ConnectionConfiguration jabberConfig = new ConnectionConfiguration(server);
|
||||||
|
jabberConfig.setSendPresence(false);
|
||||||
|
jabberConfig.setRosterLoadedAtLogin(false);
|
||||||
|
connection = new XMPPConnection(jabberConfig);
|
||||||
|
connection.connect();
|
||||||
|
connection.login(user, password);
|
||||||
|
|
||||||
|
chat = connection.getChatManager().createChat(recipient, new MessageListener() {
|
||||||
|
@Override
|
||||||
|
public void processMessage(Chat chat, Message message) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void append(LoggingEvent event) {
|
||||||
|
sendChat(layout.format(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
log.info("Closing XMPP connection");
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresLayout() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendChat(String message) {
|
||||||
|
try {
|
||||||
|
chat.sendMessage(message);
|
||||||
|
} catch (XMPPException xe) {
|
||||||
|
log.error(xe.getMessage(), xe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,593 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Tilman Walther
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.tilman.synctool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.log4j.BasicConfigurator;
|
||||||
|
import org.apache.log4j.ConsoleAppender;
|
||||||
|
import org.apache.log4j.FileAppender;
|
||||||
|
import org.apache.log4j.Level;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.log4j.PatternLayout;
|
||||||
|
import org.apache.log4j.RollingFileAppender;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
|
||||||
|
import com.martiansoftware.jsap.FlaggedOption;
|
||||||
|
import com.martiansoftware.jsap.JSAP;
|
||||||
|
import com.martiansoftware.jsap.JSAPException;
|
||||||
|
import com.martiansoftware.jsap.JSAPResult;
|
||||||
|
import com.martiansoftware.jsap.Switch;
|
||||||
|
import com.martiansoftware.jsap.UnflaggedOption;
|
||||||
|
|
||||||
|
import de.tilman.log4j.JabberAppender;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SyncTool synchronized two directories recursively.
|
||||||
|
*
|
||||||
|
* @author Tilman Walther
|
||||||
|
*/
|
||||||
|
public class SyncTool {
|
||||||
|
|
||||||
|
private final static Logger log = Logger.getLogger(SyncTool.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the different possible operations for two files in the file tree
|
||||||
|
*/
|
||||||
|
private enum Operation {
|
||||||
|
COPY, COPYDESTINATION, DELETE, NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private Connection connection;
|
||||||
|
private Statement statement;
|
||||||
|
|
||||||
|
private ResultSet resultSet;
|
||||||
|
private PreparedStatement selectFileSql;
|
||||||
|
private PreparedStatement insertFileSql;
|
||||||
|
private PreparedStatement deleteFileSql;
|
||||||
|
|
||||||
|
private boolean dryRun;
|
||||||
|
private boolean hashing;
|
||||||
|
private boolean silent;
|
||||||
|
private boolean ignoreDirAttribs;
|
||||||
|
|
||||||
|
private long filesCompared;
|
||||||
|
private long dirsCompared;
|
||||||
|
private long dirsCopied;
|
||||||
|
private long dirsDeleted;
|
||||||
|
private long filesCopied;
|
||||||
|
private long filesDeleted;
|
||||||
|
|
||||||
|
public SyncTool(JSAPResult config) {
|
||||||
|
|
||||||
|
this.dryRun = config.getBoolean("dry-run");
|
||||||
|
if (dryRun)
|
||||||
|
log.info("Performing dry-run, no changes to the file system");
|
||||||
|
|
||||||
|
this.silent = config.getBoolean("silent");
|
||||||
|
if (silent)
|
||||||
|
log.info("Silent logging");
|
||||||
|
|
||||||
|
this.ignoreDirAttribs = config.getBoolean("ignore directory attributes");
|
||||||
|
if (ignoreDirAttribs)
|
||||||
|
log.info("Ignoring directory attributes");
|
||||||
|
|
||||||
|
this.hashing = config.getBoolean("hashing");
|
||||||
|
if (hashing)
|
||||||
|
log.info("Using MD5 hashes to compare files");
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.info("Connecting to database \"" + config.getString("database file") + "\"");
|
||||||
|
Class.forName("org.h2.Driver");
|
||||||
|
|
||||||
|
connection = DriverManager.getConnection("jdbc:h2:file:" + config.getString("database file"), "sa", "");
|
||||||
|
statement = connection.createStatement();
|
||||||
|
|
||||||
|
resultSet = connection.getMetaData().getTables(null, null, "%", new String[] { "TABLE" });
|
||||||
|
if (!resultSet.next()) {
|
||||||
|
log.info("Creating database structure");
|
||||||
|
statement.execute("CREATE CACHED TABLE Source ("
|
||||||
|
+ "id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, "
|
||||||
|
+ "path VARCHAR NOT NULL, "
|
||||||
|
+ "lastSync TIMESTAMP NOT NULL, "
|
||||||
|
+ "UNIQUE (path));");
|
||||||
|
statement.execute("CREATE CACHED TABLE File ("
|
||||||
|
+ "path VARCHAR NOT NULL, "
|
||||||
|
+ "idSource INTEGER NOT NULL, "
|
||||||
|
+ "FOREIGN KEY (idSource)"
|
||||||
|
+ " REFERENCES Source(id)"
|
||||||
|
+ " ON DELETE CASCADE);");
|
||||||
|
statement.execute("CREATE INDEX IDX_ID_PATH ON File(path, idSource);");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.fatal(e.getMessage(), e);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes two directories specified by their respective paths.
|
||||||
|
*
|
||||||
|
* @param srcPath the path to the source directory
|
||||||
|
* @param destPath the path to the destination directory
|
||||||
|
*/
|
||||||
|
public void sync(String srcPath, String destPath) {
|
||||||
|
|
||||||
|
File srcRoot = new File(srcPath);
|
||||||
|
File destRoot = new File(destPath);
|
||||||
|
|
||||||
|
if (!srcRoot.isDirectory()) {
|
||||||
|
log.fatal(srcRoot + " is not a directory");
|
||||||
|
System.exit(-2);
|
||||||
|
}
|
||||||
|
if (!destRoot.isDirectory()) {
|
||||||
|
log.fatal(destRoot + " is not a directory");
|
||||||
|
System.exit(-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
String canonicalSrcPath = null;
|
||||||
|
String canonicalDestPath;
|
||||||
|
|
||||||
|
try {
|
||||||
|
canonicalSrcPath = srcRoot.getCanonicalPath();
|
||||||
|
canonicalDestPath = destRoot.getCanonicalPath();
|
||||||
|
if (canonicalSrcPath.equals(canonicalDestPath)) {
|
||||||
|
log.fatal("Source and destination point to the same directory: " + canonicalSrcPath);
|
||||||
|
System.exit(-4);
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
log.fatal(ioe.getMessage(), ioe);
|
||||||
|
System.exit(-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
resultSet = statement.executeQuery("SELECT * FROM Source WHERE path='" + canonicalSrcPath + "' LIMIT 1");
|
||||||
|
|
||||||
|
// check the database for the source directory
|
||||||
|
Integer sourceId = null;
|
||||||
|
if (resultSet.next()) {
|
||||||
|
sourceId = resultSet.getInt(1);
|
||||||
|
Timestamp lastSync = resultSet.getTimestamp(3);
|
||||||
|
log.info("Last sync for source path: " + lastSync);
|
||||||
|
} else {
|
||||||
|
log.info("Inserting new source path into database: " + canonicalSrcPath);
|
||||||
|
if (!dryRun) {
|
||||||
|
statement.executeUpdate("INSERT INTO Source (id, path, lastSync) VALUES (NULL, '" + canonicalSrcPath
|
||||||
|
+ "', CURRENT_TIMESTAMP)");
|
||||||
|
resultSet = statement.executeQuery("SELECT * FROM Source WHERE path='" + canonicalSrcPath + "' LIMIT 1");
|
||||||
|
resultSet.next();
|
||||||
|
sourceId = resultSet.getInt(1);
|
||||||
|
} else {
|
||||||
|
sourceId = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare SQL statements
|
||||||
|
selectFileSql = connection.prepareCall("SELECT * FROM File WHERE path=? AND idSource=" + sourceId + " LIMIT 1");
|
||||||
|
insertFileSql = connection.prepareCall("INSERT INTO File (idSource, path) VALUES (" + sourceId + ", ?)");
|
||||||
|
deleteFileSql = connection.prepareCall("DELETE FROM File WHERE idSource=" + sourceId + " AND path=?");
|
||||||
|
|
||||||
|
filesCompared = 0;
|
||||||
|
dirsCompared = 0;
|
||||||
|
dirsCopied = 0;
|
||||||
|
dirsDeleted = 0;
|
||||||
|
filesCopied = 0;
|
||||||
|
filesDeleted = 0;
|
||||||
|
|
||||||
|
log.info("Synchronizing " + srcRoot + " with " + destRoot);
|
||||||
|
recurse(srcRoot, destRoot);
|
||||||
|
|
||||||
|
selectFileSql.close();
|
||||||
|
insertFileSql.close();
|
||||||
|
deleteFileSql.close();
|
||||||
|
|
||||||
|
log.info("Updating source entry in database");
|
||||||
|
if (!dryRun)
|
||||||
|
statement.executeUpdate("UPDATE Source SET lastSync=CURRENT_TIMESTAMP WHERE id=" + sourceId);
|
||||||
|
|
||||||
|
statement.execute("SHUTDOWN COMPACT");
|
||||||
|
statement.close();
|
||||||
|
connection.close();
|
||||||
|
|
||||||
|
log.info("Subdirectories compared: " + dirsCompared);
|
||||||
|
log.info(" Subdirectories copied: " + dirsCopied);
|
||||||
|
log.info(" Subdirectories deleted: " + dirsDeleted);
|
||||||
|
log.info("Files compared: " + filesCompared);
|
||||||
|
log.info(" Files copied: " + filesCopied);
|
||||||
|
log.info(" Files deleted: " + filesDeleted);
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.fatal(e.getMessage(), e);
|
||||||
|
System.exit(-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private File[] srcFiles;
|
||||||
|
private HashMap<String, File> destMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes two directories recursively.
|
||||||
|
*
|
||||||
|
* @param srcDir the source directory
|
||||||
|
* @param destDir the destination directory
|
||||||
|
*/
|
||||||
|
private void recurse(File srcDir, File destDir) {
|
||||||
|
|
||||||
|
log.debug(" get listing for source directory");
|
||||||
|
srcFiles = srcDir.listFiles();
|
||||||
|
destMap = new HashMap<String, File>();
|
||||||
|
log.debug(" get listing for destination directory");
|
||||||
|
for (File file : destDir.listFiles()) {
|
||||||
|
destMap.put(file.getName(), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<File[]> recurseList = new ArrayList<File[]>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.debug(" sync source side");
|
||||||
|
for (int i = srcFiles.length - 1; i >= 0; i--) {
|
||||||
|
File srcFile = srcFiles[i];
|
||||||
|
srcFiles[i] = null;
|
||||||
|
File destFile = destMap.remove(srcFile.getName());
|
||||||
|
|
||||||
|
// check synchronization history
|
||||||
|
log.debug(" get source history from database");
|
||||||
|
selectFileSql.setString(1, srcFile.getCanonicalPath());
|
||||||
|
resultSet = selectFileSql.executeQuery();
|
||||||
|
boolean history = false;
|
||||||
|
if (resultSet.next())
|
||||||
|
history = true;
|
||||||
|
|
||||||
|
// determine what to do and do it
|
||||||
|
log.debug(" get operation");
|
||||||
|
Operation operation = getOperation(srcFile, destFile, history);
|
||||||
|
log.debug(" synchronize");
|
||||||
|
if (operation == Operation.COPYDESTINATION)
|
||||||
|
syncFileToDirectory(destFile, srcDir, Operation.COPY);
|
||||||
|
else
|
||||||
|
syncFileToDirectory(srcFile, destDir, operation);
|
||||||
|
|
||||||
|
// if the file is a directory and has not been copied or
|
||||||
|
// deleted, add for recursion
|
||||||
|
if (srcFile.isDirectory() && operation == Operation.NONE) {
|
||||||
|
log.debug(" adding directory for recursion");
|
||||||
|
if (destFile == null) {
|
||||||
|
destFile = new File(destDir, srcFile.getName());
|
||||||
|
}
|
||||||
|
recurseList.add(new File[] { srcFile, destFile });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// opposite direction: process remaining files from destination
|
||||||
|
log.debug(" sync destination side");
|
||||||
|
for (File destFile : destMap.values()) {
|
||||||
|
// check synchronization history
|
||||||
|
selectFileSql.setString(1, new File(srcDir, destFile.getName()).getCanonicalPath());
|
||||||
|
log.debug(" get history");
|
||||||
|
resultSet = selectFileSql.executeQuery();
|
||||||
|
boolean history = false;
|
||||||
|
if (resultSet.next())
|
||||||
|
history = true;
|
||||||
|
|
||||||
|
log.debug(" synchronize");
|
||||||
|
syncFileToDirectory(destFile, srcDir, getOperation(destFile, null, history));
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse all directories that have not been deleted or entirely
|
||||||
|
// copied
|
||||||
|
for (File[] recurseDir : recurseList) {
|
||||||
|
if (!silent)
|
||||||
|
log.info("Entering directory " + recurseDir[0]);
|
||||||
|
recurse(recurseDir[0], recurseDir[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.fatal(e.getMessage(), e);
|
||||||
|
System.exit(-7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conducts the specified operation for the file.
|
||||||
|
*
|
||||||
|
* @param file the file to be processed
|
||||||
|
* @param directory the target directory
|
||||||
|
* @param operation the operation to be executed
|
||||||
|
*/
|
||||||
|
private void syncFileToDirectory(File file, File directory, Operation operation) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (operation == Operation.NONE) {
|
||||||
|
if (!silent)
|
||||||
|
log.info("No operation for " + file);
|
||||||
|
return;
|
||||||
|
} else if (operation == Operation.COPY) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
log.info("Copying directory " + file);
|
||||||
|
if (!dryRun)
|
||||||
|
FileUtils.copyDirectoryToDirectory(file, directory);
|
||||||
|
dirsCopied++;
|
||||||
|
} else {
|
||||||
|
log.info("Copying file " + file);
|
||||||
|
if (!dryRun)
|
||||||
|
FileUtils.copyFileToDirectory(file, directory);
|
||||||
|
filesCopied++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (operation == Operation.DELETE) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
log.info("Deleting directory " + file);
|
||||||
|
if (!dryRun)
|
||||||
|
FileUtils.deleteDirectory(file);
|
||||||
|
dirsDeleted++;
|
||||||
|
} else {
|
||||||
|
log.info("Deleting file " + file);
|
||||||
|
if (!dryRun)
|
||||||
|
file.delete();
|
||||||
|
filesDeleted++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
log.fatal(ioe.getMessage(), ioe);
|
||||||
|
System.exit(-8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines what to do with two files at the same place in the file tree
|
||||||
|
* on the source and the destination. This method also updates the database
|
||||||
|
* for the source file.
|
||||||
|
*/
|
||||||
|
private Operation getOperation(File srcFile, File destFile, boolean history) throws SQLException, IOException {
|
||||||
|
|
||||||
|
if (destFile != null && destFile.exists()) {
|
||||||
|
|
||||||
|
if (!history) {
|
||||||
|
insertFileSql.setString(1, srcFile.getCanonicalPath());
|
||||||
|
if (!dryRun)
|
||||||
|
insertFileSql.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcFile.isDirectory()) {
|
||||||
|
dirsCompared++;
|
||||||
|
if (!dryRun && !ignoreDirAttribs) {
|
||||||
|
if (srcFile.lastModified() != destFile.lastModified()
|
||||||
|
// || srcFile.canExecute() != destFile.canExecute()
|
||||||
|
// || srcFile.canRead() != destFile.canRead()
|
||||||
|
// || srcFile.canWrite() != destFile.canWrite()
|
||||||
|
) {
|
||||||
|
log.info("Setting attributes for " + destFile);
|
||||||
|
destFile.setLastModified(srcFile.lastModified());
|
||||||
|
// destFile.setExecutable(srcFile.canExecute());
|
||||||
|
// destFile.setReadable(srcFile.canRead());
|
||||||
|
// destFile.setWritable(srcFile.canWrite());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Operation.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
filesCompared++;
|
||||||
|
|
||||||
|
if (consideredEqual(srcFile, destFile))
|
||||||
|
return Operation.NONE;
|
||||||
|
|
||||||
|
if (srcFile.lastModified() > destFile.lastModified())
|
||||||
|
return Operation.COPY; // copy source file
|
||||||
|
|
||||||
|
return Operation.COPYDESTINATION; // copy destination file
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the file exists in the history, it has been deleted on the
|
||||||
|
// target side and should also be deleted on the source side
|
||||||
|
if (history) {
|
||||||
|
deleteFileSql.setString(1, srcFile.getCanonicalPath());
|
||||||
|
if (!dryRun)
|
||||||
|
deleteFileSql.execute();
|
||||||
|
return Operation.DELETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the file is not present in the synchronization history, it
|
||||||
|
// has been added on the source side and should be copied
|
||||||
|
insertFileSql.setString(1, srcFile.getCanonicalPath());
|
||||||
|
if (!dryRun)
|
||||||
|
insertFileSql.execute();
|
||||||
|
return Operation.COPY; // copy source file
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether two files at the same place in the file tree are
|
||||||
|
* considered to be equal under the given parameters.
|
||||||
|
* @throws IOException
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
*/
|
||||||
|
private boolean consideredEqual(File src, File dest) throws FileNotFoundException, IOException {
|
||||||
|
// TODO add a certain amount to the source timestamp, if defined by parameter
|
||||||
|
// TODO allow a certain difference for the timestamps, if defined by parameter
|
||||||
|
if ((src.lastModified() == dest.lastModified()) && (src.length() == dest.length())) {
|
||||||
|
if (!hashing)
|
||||||
|
return true;
|
||||||
|
if (DigestUtils.md5Hex(new FileInputStream(src)).equals(DigestUtils.md5Hex(new FileInputStream(dest))))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d{ISO8601} - %m%n")));
|
||||||
|
log.info("Starting SyncTool version 1.2.1");
|
||||||
|
|
||||||
|
JSAP jsap = new JSAP();
|
||||||
|
|
||||||
|
try {
|
||||||
|
UnflaggedOption sourceOption = new UnflaggedOption("source path").setStringParser(JSAP.STRING_PARSER).setRequired(
|
||||||
|
true);
|
||||||
|
sourceOption.setHelp("the source path");
|
||||||
|
jsap.registerParameter(sourceOption);
|
||||||
|
|
||||||
|
UnflaggedOption destinationOption = new UnflaggedOption("destination path").setStringParser(JSAP.STRING_PARSER)
|
||||||
|
.setRequired(true);
|
||||||
|
destinationOption.setHelp("the destination path");
|
||||||
|
jsap.registerParameter(destinationOption);
|
||||||
|
|
||||||
|
FlaggedOption dbFileOption = new FlaggedOption("database file").setStringParser(JSAP.STRING_PARSER).setLongFlag(
|
||||||
|
"dbfile").setShortFlag('f').setDefault("synctool");
|
||||||
|
dbFileOption.setHelp("the path to the database file to use");
|
||||||
|
jsap.registerParameter(dbFileOption);
|
||||||
|
|
||||||
|
FlaggedOption logfileOption = new FlaggedOption("logfile").setStringParser(JSAP.STRING_PARSER)
|
||||||
|
.setLongFlag("logfile").setShortFlag('l');
|
||||||
|
logfileOption.setHelp("the path for a logfile to write");
|
||||||
|
jsap.registerParameter(logfileOption);
|
||||||
|
|
||||||
|
FlaggedOption jabberAddress = new FlaggedOption("jabber address").setStringParser(JSAP.STRING_PARSER).setLongFlag(
|
||||||
|
"jabber").setShortFlag('j');
|
||||||
|
jabberAddress.setHelp("send logging output as jabber message to the given address");
|
||||||
|
jsap.registerParameter(jabberAddress);
|
||||||
|
|
||||||
|
FlaggedOption jabberServer = new FlaggedOption("jabber server").setStringParser(JSAP.STRING_PARSER).setLongFlag(
|
||||||
|
"server").setShortFlag('r');
|
||||||
|
jabberServer.setHelp("the jabber server to connect to");
|
||||||
|
jsap.registerParameter(jabberServer);
|
||||||
|
|
||||||
|
FlaggedOption jabberUser = new FlaggedOption("jabber user").setStringParser(JSAP.STRING_PARSER).setLongFlag(
|
||||||
|
"user").setShortFlag('u');
|
||||||
|
jabberUser.setHelp("the jabber user name used for logging in to the server");
|
||||||
|
jsap.registerParameter(jabberUser);
|
||||||
|
|
||||||
|
FlaggedOption jabberPassword = new FlaggedOption("jabber password").setStringParser(JSAP.STRING_PARSER).setLongFlag(
|
||||||
|
"password").setShortFlag('p');
|
||||||
|
jabberPassword.setHelp("the jabber password used for logging in to the server");
|
||||||
|
jsap.registerParameter(jabberPassword);
|
||||||
|
|
||||||
|
Switch dryRunSwitch = new Switch("dry-run").setLongFlag("dry-run").setShortFlag('d');
|
||||||
|
dryRunSwitch.setHelp("perform a trial run with no changes made");
|
||||||
|
jsap.registerParameter(dryRunSwitch);
|
||||||
|
|
||||||
|
Switch hashingSwitch = new Switch("hashing").setLongFlag("hashing").setShortFlag('h');
|
||||||
|
hashingSwitch.setHelp("generate MD5 file hashes for exact comparison");
|
||||||
|
jsap.registerParameter(hashingSwitch);
|
||||||
|
|
||||||
|
Switch rollingSwitch = new Switch("rolling-logfile").setLongFlag("rolling-logfile").setShortFlag('o');
|
||||||
|
rollingSwitch.setHelp("generate a rolling logfile with a maximum size of 10 MB");
|
||||||
|
jsap.registerParameter(rollingSwitch);
|
||||||
|
|
||||||
|
Switch ignoreDirAttribsSwitch = new Switch("ignore directory attributes").setLongFlag("ignore-directory-attributes").setShortFlag('i');
|
||||||
|
ignoreDirAttribsSwitch.setHelp("do not copy attributes for directories");
|
||||||
|
jsap.registerParameter(ignoreDirAttribsSwitch);
|
||||||
|
|
||||||
|
Switch silentSwitch = new Switch("silent").setLongFlag("silent").setShortFlag('s');
|
||||||
|
silentSwitch.setHelp("do not print \"Entering directory\" and \"No operation\" messages");
|
||||||
|
jsap.registerParameter(silentSwitch);
|
||||||
|
|
||||||
|
FlaggedOption checkFileExists = new FlaggedOption("checkfile").setStringParser(JSAP.STRING_PARSER).setLongFlag(
|
||||||
|
"check-file-exists");
|
||||||
|
checkFileExists.setHelp("perfom synchronization only if the given file exists");
|
||||||
|
jsap.registerParameter(checkFileExists);
|
||||||
|
|
||||||
|
Switch debugSwitch = new Switch("debug").setLongFlag("debug");
|
||||||
|
debugSwitch.setHelp("print debug messages");
|
||||||
|
jsap.registerParameter(debugSwitch);
|
||||||
|
|
||||||
|
Switch helpSwitch = new Switch("help").setLongFlag("help").setShortFlag('?');
|
||||||
|
helpSwitch.setHelp("print help and exit");
|
||||||
|
jsap.registerParameter(helpSwitch);
|
||||||
|
|
||||||
|
} catch (JSAPException e) {
|
||||||
|
log.fatal(e.getMessage());
|
||||||
|
System.exit(-1002);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSAPResult config = jsap.parse(args);
|
||||||
|
|
||||||
|
if (!config.success() || config.getBoolean("help")) {
|
||||||
|
for (java.util.Iterator errs = config.getErrorMessageIterator(); errs.hasNext();) {
|
||||||
|
log.error("Error: " + errs.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.error("Usage: java -jar synctool.jar " + jsap.getUsage() + "\n\n" + jsap.getHelp());
|
||||||
|
System.exit(-1003);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.getBoolean("debug")) {
|
||||||
|
log.setLevel(Level.DEBUG);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.setLevel(Level.INFO);
|
||||||
|
}
|
||||||
|
log.info("Log level set to " + log.getLevel());
|
||||||
|
|
||||||
|
if (config.getString("logfile") != null) {
|
||||||
|
try {
|
||||||
|
if (config.getBoolean("rolling-logfile")) {
|
||||||
|
BasicConfigurator.configure(new RollingFileAppender(new PatternLayout("%d{ISO8601} - %m%n"), config.getString("logfile")));
|
||||||
|
log.info("Logging to rolling logfile " + config.getString("logfile"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BasicConfigurator.configure(new FileAppender(new PatternLayout("%d{ISO8601} - %m%n"), config.getString("logfile")));
|
||||||
|
log.info("Logging to " + config.getString("logfile"));
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
log.fatal(ioe.getMessage(), ioe);
|
||||||
|
System.exit(-1004);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.getString("jabber address") != null) {
|
||||||
|
try {
|
||||||
|
BasicConfigurator.configure(new JabberAppender(config.getString("jabber address"), config.getString("jabber server"), config.getString("jabber user"), config.getString("jabber password")));
|
||||||
|
} catch (XMPPException xe) {
|
||||||
|
log.fatal(xe.getMessage(), xe);
|
||||||
|
System.exit(-1005);
|
||||||
|
}
|
||||||
|
log.info("Logging to " + config.getString("jabber address"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.getString("checkfile") != null) {
|
||||||
|
if (new File(config.getString("checkfile")).exists() == false) {
|
||||||
|
log.error("The file " + config.getString("checkfile") + " does not exist. Stopping synchronization.");
|
||||||
|
System.exit(-1006);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncTool syncTool = new SyncTool(config);
|
||||||
|
syncTool.sync(config.getString("source path"), config.getString("destination path"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package de.tilman.sync;
|
||||||
|
|
||||||
|
import junit.framework.Test;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.framework.TestSuite;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for simple App.
|
||||||
|
*/
|
||||||
|
public class AppTest
|
||||||
|
extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the test case
|
||||||
|
*
|
||||||
|
* @param testName name of the test case
|
||||||
|
*/
|
||||||
|
public AppTest( String testName )
|
||||||
|
{
|
||||||
|
super( testName );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the suite of tests being tested
|
||||||
|
*/
|
||||||
|
public static Test suite()
|
||||||
|
{
|
||||||
|
return new TestSuite( AppTest.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rigourous Test :-)
|
||||||
|
*/
|
||||||
|
public void testApp()
|
||||||
|
{
|
||||||
|
assertTrue( true );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#Generated by Maven
|
||||||
|
#Mon Mar 21 17:33:34 CET 2011
|
||||||
|
version=1.0.8
|
||||||
|
groupId=SyncTool
|
||||||
|
artifactId=synctool
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<testsuite failures="0" time="0.008" errors="0" skipped="0" tests="1" name="de.tilman.sync.AppTest">
|
||||||
|
<properties>
|
||||||
|
<property name="java.runtime.name" value="Java(TM) SE Runtime Environment"/>
|
||||||
|
<property name="sun.boot.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/amd64"/>
|
||||||
|
<property name="java.vm.version" value="19.1-b02"/>
|
||||||
|
<property name="java.vm.vendor" value="Sun Microsystems Inc."/>
|
||||||
|
<property name="java.vendor.url" value="http://java.sun.com/"/>
|
||||||
|
<property name="path.separator" value=":"/>
|
||||||
|
<property name="java.vm.name" value="Java HotSpot(TM) 64-Bit Server VM"/>
|
||||||
|
<property name="file.encoding.pkg" value="sun.io"/>
|
||||||
|
<property name="user.country" value="US"/>
|
||||||
|
<property name="sun.java.launcher" value="SUN_STANDARD"/>
|
||||||
|
<property name="sun.os.patch.level" value="unknown"/>
|
||||||
|
<property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
|
||||||
|
<property name="user.dir" value="/home/tilman/workspace-new/SyncTool"/>
|
||||||
|
<property name="java.runtime.version" value="1.6.0_24-b07"/>
|
||||||
|
<property name="java.awt.graphicsenv" value="sun.awt.X11GraphicsEnvironment"/>
|
||||||
|
<property name="basedir" value="/home/tilman/workspace-new/SyncTool"/>
|
||||||
|
<property name="java.endorsed.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/endorsed"/>
|
||||||
|
<property name="os.arch" value="amd64"/>
|
||||||
|
<property name="surefire.real.class.path" value="/home/tilman/workspace-new/SyncTool/target/surefire/surefirebooter8306257726018021915.jar"/>
|
||||||
|
<property name="java.io.tmpdir" value="/tmp"/>
|
||||||
|
<property name="line.separator" value="
|
||||||
|
"/>
|
||||||
|
<property name="java.vm.specification.vendor" value="Sun Microsystems Inc."/>
|
||||||
|
<property name="os.name" value="Linux"/>
|
||||||
|
<property name="sun.jnu.encoding" value="UTF-8"/>
|
||||||
|
<property name="java.library.path" value="/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/amd64/server:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/amd64:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/../lib/amd64:/usr/lib/xulrunner-1.9.2.15:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib"/>
|
||||||
|
<property name="surefire.test.class.path" value="/home/tilman/workspace-new/SyncTool/target/test-classes:/home/tilman/workspace-new/SyncTool/target/classes:/home/tilman/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar:/home/tilman/.m2/repository/com/martiansoftware/jsap/2.1/jsap-2.1.jar:/home/tilman/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:/home/tilman/.m2/repository/com/h2database/h2/1.3.150/h2-1.3.150.jar:/home/tilman/.m2/repository/commons-codec/commons-codec/1.4/commons-codec-1.4.jar:/home/tilman/.m2/repository/commons-io/commons-io/2.0.1/commons-io-2.0.1.jar:/home/tilman/.m2/repository/jivesoftware/smack/3.1.0/smack-3.1.0.jar:/home/tilman/.m2/repository/jivesoftware/smackx/3.1.0/smackx-3.1.0.jar:/home/tilman/.m2/repository/org/hsqldb/hsqldb/2.0.0/hsqldb-2.0.0.jar:"/>
|
||||||
|
<property name="java.specification.name" value="Java Platform API Specification"/>
|
||||||
|
<property name="java.class.version" value="50.0"/>
|
||||||
|
<property name="sun.management.compiler" value="HotSpot 64-Bit Server Compiler"/>
|
||||||
|
<property name="os.version" value="2.6.35-27-generic"/>
|
||||||
|
<property name="user.home" value="/home/tilman"/>
|
||||||
|
<property name="user.timezone" value=""/>
|
||||||
|
<property name="java.awt.printerjob" value="sun.print.PSPrinterJob"/>
|
||||||
|
<property name="file.encoding" value="UTF-8"/>
|
||||||
|
<property name="java.specification.version" value="1.6"/>
|
||||||
|
<property name="user.name" value="tilman"/>
|
||||||
|
<property name="java.class.path" value="/home/tilman/workspace-new/SyncTool/target/test-classes:/home/tilman/workspace-new/SyncTool/target/classes:/home/tilman/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar:/home/tilman/.m2/repository/com/martiansoftware/jsap/2.1/jsap-2.1.jar:/home/tilman/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:/home/tilman/.m2/repository/com/h2database/h2/1.3.150/h2-1.3.150.jar:/home/tilman/.m2/repository/commons-codec/commons-codec/1.4/commons-codec-1.4.jar:/home/tilman/.m2/repository/commons-io/commons-io/2.0.1/commons-io-2.0.1.jar:/home/tilman/.m2/repository/jivesoftware/smack/3.1.0/smack-3.1.0.jar:/home/tilman/.m2/repository/jivesoftware/smackx/3.1.0/smackx-3.1.0.jar:/home/tilman/.m2/repository/org/hsqldb/hsqldb/2.0.0/hsqldb-2.0.0.jar:"/>
|
||||||
|
<property name="java.vm.specification.version" value="1.0"/>
|
||||||
|
<property name="sun.arch.data.model" value="64"/>
|
||||||
|
<property name="java.home" value="/usr/lib/jvm/java-6-sun-1.6.0.24/jre"/>
|
||||||
|
<property name="java.specification.vendor" value="Sun Microsystems Inc."/>
|
||||||
|
<property name="user.language" value="en"/>
|
||||||
|
<property name="java.vm.info" value="mixed mode"/>
|
||||||
|
<property name="java.version" value="1.6.0_24"/>
|
||||||
|
<property name="java.ext.dirs" value="/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/ext:/usr/java/packages/lib/ext"/>
|
||||||
|
<property name="sun.boot.class.path" value="/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/resources.jar:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/rt.jar:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/jsse.jar:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/jce.jar:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/charsets.jar:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-sun-1.6.0.24/jre/classes"/>
|
||||||
|
<property name="java.vendor" value="Sun Microsystems Inc."/>
|
||||||
|
<property name="localRepository" value="/home/tilman/.m2/repository"/>
|
||||||
|
<property name="file.separator" value="/"/>
|
||||||
|
<property name="java.vendor.url.bug" value="http://java.sun.com/cgi-bin/bugreport.cgi"/>
|
||||||
|
<property name="sun.cpu.endian" value="little"/>
|
||||||
|
<property name="sun.io.unicode.encoding" value="UnicodeLittle"/>
|
||||||
|
<property name="sun.desktop" value="gnome"/>
|
||||||
|
<property name="sun.cpu.isalist" value=""/>
|
||||||
|
</properties>
|
||||||
|
<testcase time="0.001" classname="de.tilman.sync.AppTest" name="testApp"/>
|
||||||
|
</testsuite>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Test set: de.tilman.sync.AppTest
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.008 sec
|
||||||
Reference in New Issue
Block a user