package com.dbxml.db.enterprise.sync;
/*
* dbXML - Native XML Database
* Copyright (c) 1999-2006 The dbXML Group, L.L.C.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* $Id: WorkerThread.java,v 1.8 2006/02/02 19:04:15 bradford Exp $
*/
import java.io.*;
import java.util.*;
import com.dbxml.db.client.CollectionClient;
import com.dbxml.db.client.dbXMLClient;
import com.dbxml.labrador.types.Variant;
import com.dbxml.xml.dom.DOMHelper;
import com.dbxml.xml.dom.DOMUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* WorkerThread
*/
public final class WorkerThread extends Thread {
private static final String[] EmptyStrings = new String[0];
private String currentGroup = "";
private String currentFile = "";
private int percentComplete;
private boolean cancel;
private boolean error;
private List log = new ArrayList();
private Set groups;
private SyncManagerClient sourceSyncClient;
private SyncManagerClient localSyncClient;
private dbXMLClient sourceContentClient;
private dbXMLClient localContentClient;
private Date lastSyncDate;
public SyncManagerClient getSourceSyncClient() {
return sourceSyncClient;
}
public void setSourceSyncClient(SyncManagerClient sourceSyncClient) {
this.sourceSyncClient = sourceSyncClient;
}
public SyncManagerClient getLocalSyncClient() {
return localSyncClient;
}
public void setLocalSyncClient(SyncManagerClient localSyncClient) {
this.localSyncClient = localSyncClient;
}
public dbXMLClient getSourceContentClient() {
return sourceContentClient;
}
public void setSourceContentClient(dbXMLClient sourceContentClient) {
this.sourceContentClient = sourceContentClient;
}
public dbXMLClient getLocalContentClient() {
return localContentClient;
}
public void setLocalContentClient(dbXMLClient localContentClient) {
this.localContentClient = localContentClient;
}
public Date getLastSyncDate() {
return lastSyncDate;
}
public String getCurrentGroup() {
return currentGroup;
}
public String getCurrentFile() {
return currentFile;
}
public int getPercentComplete() {
return percentComplete;
}
public List getLog() {
return log;
}
public boolean isError() {
return error;
}
public void cancelWork() {
cancel = true;
}
public String[] listGroups() {
if ( groups != null )
return (String[])groups.toArray(EmptyStrings);
else
return EmptyStrings;
}
public void addGroup(String group) {
if ( groups == null )
groups = new TreeSet();
if ( !groups.contains(group) )
groups.add(group);
}
public void removeGroup(String group) {
if ( groups != null && groups.contains(groups) ) {
groups.remove(group);
if ( groups.isEmpty() )
groups = null;
}
}
public void run() {
cancel = false;
Map tempFiles = new HashMap(); // String to File
Map delList = new HashMap(); // String to CollectionClient
Map lclColCache = new HashMap(); // String to CollectionClient
Set lclColXML = new TreeSet(); // String
Map srcColCache = new HashMap(); // String to CollectionClient
Set srcColXML = new TreeSet(); // String
String src_host = sourceContentClient.getProperty(dbXMLClient.HOST)
+ ':' + sourceContentClient.getProperty(dbXMLClient.PORT);
lastSyncDate = new Date(0);
try {
localSyncClient.getMachineID();
Document doc = localContentClient.getContent("/system/SysConfig/sync" + src_host + ".xml").getDocument();
Element e = doc.getDocumentElement();
lastSyncDate = new Variant(e.getAttribute("date")).getDate();
}
catch ( Exception e ) {
// Document doesn't exist I guess
//out.println(e.getMessage());
}
try {
// Do Stuff
Manifest mf;
if ( groups != null )
mf = sourceSyncClient.listGroupChanges(lastSyncDate, (String[])groups.toArray(EmptyStrings));
else
mf = sourceSyncClient.listAllChanges(lastSyncDate);
Date startTime = mf.getDate();
String[] grps = mf.getGroupNames();
int totalDocs = 0;
for ( int i = 0; i < grps.length; i++ )
totalDocs += mf.getGroupContent(grps[i]).length;
if ( grps.length > 0 ) {
log.add("Updating " + grps.length + " files");
int docNum = 0;
for ( int i = 0; !cancel && i < grps.length; i++ ) {
currentGroup = grps[i];
String[] files = mf.getGroupContent(currentGroup);
log.add("Group '" + currentGroup + "' contains " + files.length + " updated files");
for ( int j = 0; !cancel && j < files.length; j++ ) {
currentFile = files[j];
int idx = currentFile.lastIndexOf('/');
String colName = currentFile.substring(0, idx);
String docName = currentFile.substring(idx + 1);
Content c = mf.getContent(currentFile);
CollectionClient lclCol = (CollectionClient)lclColCache.get(colName);
if ( lclCol == null ) {
lclCol = localContentClient.getCollection(colName);
lclColCache.put(colName, lclCol);
if ( lclCol.getCollectionType() == CollectionClient.TYPE_DOCUMENTS )
lclColXML.add(colName);
}
if ( c.getStatus() != Constants.STATUS_DELETED ) {
CollectionClient srcCol = (CollectionClient)srcColCache.get(colName);
if ( srcCol == null ) {
srcCol = sourceContentClient.getCollection(colName);
srcColCache.put(colName, srcCol);
if ( srcCol.getCollectionType() == CollectionClient.TYPE_DOCUMENTS )
srcColXML.add(colName);
}
File f = File.createTempFile("dbxml_", ".sync");
tempFiles.put(currentFile, f);
if ( srcColXML.contains(colName) ) {
String doc = srcCol.getDocumentAsText(docName);
FileOutputStream fos = new FileOutputStream(f);
BufferedOutputStream bos = new BufferedOutputStream(fos, 4096);
OutputStreamWriter osw = new OutputStreamWriter(bos, "UTF8");
osw.write(doc);
osw.flush();
osw.close();
}
else {
byte[] doc = srcCol.getValue(docName);
FileOutputStream fos = new FileOutputStream(f);
fos.write(doc);
fos.close();
}
}
else
delList.put(currentFile, lclCol);
percentComplete = 100 * docNum++ / totalDocs;
}
}
if ( !cancel ) {
Iterator iter = delList.entrySet().iterator();
while ( iter.hasNext() ) {
Map.Entry entry = (Map.Entry)iter.next();
String path = (String)entry.getKey();
CollectionClient col = (CollectionClient)entry.getValue();
int idx = path.lastIndexOf('/');
String docName = path.substring(idx + 1);
col.remove(docName);
}
iter = tempFiles.entrySet().iterator();
while ( iter.hasNext() ) {
Map.Entry entry = (Map.Entry)iter.next();
String path = (String)entry.getKey();
File f = (File)entry.getValue();
int idx = path.lastIndexOf('/');
String colName = path.substring(0, idx);
String docName = path.substring(idx + 1);
CollectionClient lclCol = (CollectionClient)lclColCache.get(colName);
if ( lclCol == null ) {
lclCol = localContentClient.getCollection(colName);
lclColCache.put(colName, lclCol);
if ( lclCol.getCollectionType() == CollectionClient.TYPE_DOCUMENTS )
lclColXML.add(colName);
}
if ( lclColXML.contains(colName) ) {
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis, 4096);
InputStreamReader isr = new InputStreamReader(bis, "UTF8");
char[] buf = new char[(int)f.length()];
int size = isr.read(buf);
isr.close();
lclCol.setDocumentAsText(docName, new String(buf, 0, size));
}
else {
FileInputStream fis = new FileInputStream(f);
byte[] buf = new byte[(int)f.length()];
fis.read(buf);
fis.close();
lclCol.setValue(docName, buf);
}
}
}
log.add("Synchronization complete");
}
else
log.add("No files are out of date");
if ( !cancel ) {
// Write the last sync to the Database
Document doc = DOMHelper.newDocument();
Element srvElem = doc.createElement("server");
doc.appendChild(srvElem);
srvElem.setAttribute("host", src_host);
srvElem.setAttribute("date", new Variant(startTime).getString());
for ( int i = 0; i < log.size(); i++ ) {
Element logElem = doc.createElement("log");
DOMUtils.setText(logElem, (String)log.get(i));
srvElem.appendChild(logElem);
}
CollectionClient col = localContentClient.getCollection("/system/SysConfig");
col.setDocument("sync" + src_host + ".xml", doc);
}
}
catch ( Exception e ) {
error = true;
log.add("Exception: " + e.getMessage());
log.add("Group: " + currentGroup + " -- File: " + currentFile);
log.add("Synchronization cancelled");
}
finally {
// Delete any temporary files
Iterator iter = tempFiles.values().iterator();
while ( iter.hasNext() ) {
try {
File f = (File)iter.next();
f.delete();
}
catch ( Exception e ) {
// Whatever
}
}
}
}
}