package com.aionemu.packetsamurai.logrepo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javolution.util.FastMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import com.aionemu.packetsamurai.PacketSamurai;
import com.aionemu.packetsamurai.gui.Main;
import com.aionemu.packetsamurai.gui.logrepo.LogFilesTab;
/**
*
* @author Gilles Duboscq
*
*/
public class LogRepository
{
private static String REPOSITORY_FILE = "logrepository.xml";
private File _localLogsDir;
private Map<String, LogFile> _localLogs = new FastMap<String, LogFile>();
private Map<String, LogFile> _remoteLogs = new FastMap<String, LogFile>();
private int _lastId;
private static class SingletonHolder
{
private final static LogRepository singleton = new LogRepository();
}
public static LogRepository getInstance()
{
return SingletonHolder.singleton;
}
private LogRepository()
{
_localLogsDir = new File("./logs");
this.loadFromDatabase();
}
private void loadFromDatabase()
{
Document doc;
File file = new File(REPOSITORY_FILE);
Node root = null;
if (file.exists())
{
try
{
FileInputStream fis = new FileInputStream(file);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setIgnoringComments(true);
DocumentBuilder docBuilder = factory.newDocumentBuilder();
doc = docBuilder.parse(fis);
root = doc.getDocumentElement();
fis.close();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (ParserConfigurationException e)
{
e.printStackTrace();
}
catch (SAXException e)
{
e.printStackTrace();
}
if (root != null)
{
for (Node n = root.getFirstChild(); n != null; n = n.getNextSibling())
{
if (n.getNodeName().equalsIgnoreCase("local"))
{
this.parseLocalLogs(n);
}
else if (n.getNodeName().equalsIgnoreCase("remote"))
{
this.parseRemoteLogs(n);
}
}
}
}
}
@SuppressWarnings("unused")
private void loadFromDisk()
{
this.refreshLocalLogs();
}
private void parseLocalLogs(Node node)
{
NamedNodeMap attributes;
int id;
@SuppressWarnings("unused")
long filesize;
String uploader, filename;
boolean isRemote;
for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling())
{
if (n.getNodeName().equalsIgnoreCase("log"))
{
attributes = n.getAttributes();
filename = attributes.getNamedItem("filename").getNodeValue();
filesize = Long.parseLong(attributes.getNamedItem("filesize").getNodeValue());
isRemote = Boolean.parseBoolean( attributes.getNamedItem("isRemote").getNodeValue());
File file = new File(this.getLogsDir()+"/"+filename);
LogFile logFile = new LogFile(file, isRemote);
if (file.exists())
{
if (isRemote)
{
id = Integer.parseInt(attributes.getNamedItem("id").getNodeValue());
uploader = attributes.getNamedItem("uploader").getNodeValue();
logFile.setRemoteUploader(uploader);
logFile.setRemoteId(id);
}
logFile.setComments(attributes.getNamedItem("comments").getNodeValue());
this.addLocalLog(logFile);
}
}
}
}
private void parseRemoteLogs(Node node)
{
NamedNodeMap attributes;
int id;
long filesize;
String uploader, filename, protocol;
for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling())
{
if (n.getNodeName().equalsIgnoreCase("log"))
{
attributes = n.getAttributes();
id = Integer.parseInt(attributes.getNamedItem("id").getNodeValue());
filename = attributes.getNamedItem("filename").getNodeValue();
filesize = Long.parseLong(attributes.getNamedItem("filesize").getNodeValue());
uploader = attributes.getNamedItem("uploader").getNodeValue();
LogFile logFile = new LogFile(filename, filesize, false, true);
logFile.setRemoteUploader(uploader);
logFile.setRemoteId(id);
logFile.setComments(attributes.getNamedItem("comments").getNodeValue());
if (attributes.getNamedItem("protocol") != null)
{
protocol = attributes.getNamedItem("protocol").getNodeValue();
logFile.setRemoteProtocolName(protocol);
// discard logs from old logger version that didnt saved the protocol (they will get refreshed from remote repo)
this.addRemoteLog(logFile);
}
}
}
}
public File getLogsDir()
{
return _localLogsDir;
}
public void refreshLocalLogs()
{
if (!_localLogsDir.isDirectory())
throw new IllegalStateException("The local logs dir must be a directory");
int size = _localLogs.size();
for (File log : _localLogsDir.listFiles(new LogFileFilter()))
{
if (!_localLogs.containsKey(log.getName()))
{
try
{
LogFile logFile = new LogFile(log,false);
logFile.loadHeaders();
logFile.checkRemote();
this.addLocalLog(logFile);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
if (_localLogs.size() > size)
{
this.runSaveDatabase();
}
}
public void refreshRemoteLogs()
{
int size = _remoteLogs.size();
RemoteLogRepositoryBackend.getInstance().updateRemoteLogsList(this.getLastId()+1);
if (_remoteLogs.size() > size)
{
this.runSaveDatabase();
}
}
public void addLocalLog(LogFile logFile)
{
_localLogs.put(logFile.getName(), logFile);
if (_remoteLogs.containsKey(logFile.getName()))
{
if (!_remoteLogs.get(logFile.getName()).isRemote())
{
throw new IllegalStateException("This log was suposed to be remote: "+logFile.getName());
}
_remoteLogs.remove(logFile.getName());
}
this.compareAndSet(logFile.getRemoteId());
}
public void addRemoteLog(LogFile logFile)
{
if (_localLogs.containsKey(logFile.getName()))
{
LogFile localLog = _localLogs.get(logFile.getName());
localLog.setRemote(true);
this.updateLogInfo(logFile, localLog);
}
else
{
if (!_remoteLogs.containsKey(logFile.getName()))
{
LogFilesTab filesTab = ((Main)PacketSamurai.getUserInterface()).getLogRepoTab().getLogFilesTab();
filesTab.addLogFile(logFile);
}
else
{
LogFile localLog = _remoteLogs.get(logFile.getName());
this.updateLogInfo(logFile, localLog);
}
_remoteLogs.put(logFile.getName(), logFile);
}
this.compareAndSet(logFile.getRemoteId());
}
public void updateLogInfo(LogFile srcLogFile, LogFile dstLogFile)
{
dstLogFile.setRemoteUploader(srcLogFile.getRemoteUploader());
dstLogFile.setComments(srcLogFile.getComments());
dstLogFile.setRemoteAnalyserBits(srcLogFile.getAnalyserBitSet());
dstLogFile.setRemoteId(srcLogFile.getRemoteId());
dstLogFile.setUploadedTime(srcLogFile.getUploadedTime());
}
public Collection<LogFile> getLocalLogs()
{
return _localLogs.values();
}
public Collection<LogFile> getRemoteLogs()
{
return _remoteLogs.values();
}
private synchronized void compareAndSet(int id)
{
if (_lastId < id)
{
_lastId = id;
}
}
public synchronized int getLastId()
{
return _lastId;
}
private class LogFileFilter implements FilenameFilter
{
public boolean accept(File arg0, String name)
{
if (name.endsWith(".cap") || name.endsWith(".pcap"))
return true;
return false;
}
}
public void runSaveDatabase()
{
Thread save = new Thread
(
new Runnable()
{
public void run()
{
LogRepository.this.saveDatabase();
}
}
);
save.start();
}
private synchronized void saveDatabase()
{
try
{
TransformerFactory transformFac = TransformerFactory.newInstance();
//trans.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2");
transformFac.setAttribute("indent-number", 4);
Transformer trans = transformFac.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
// Fill Xml elements
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
factory.setIgnoringComments(true);
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
//DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//Document doc = docBuilder.newDocument();
/*OutputFormat of = new OutputFormat("XML","UTF-8",true);
of.setIndent(1);
of.setIndenting(true);
of.setLineWidth(0);*/
Element root = doc.createElement("repository");
Element local = doc.createElement("local");
doc.appendChild(root);
root.appendChild(local);
Element log;
for (LogFile logFile : this.getLocalLogs())
{
log = doc.createElement("log");
log.setAttribute("filename",logFile.getName());
log.setAttribute("filesize",Long.toString(logFile.getSize()));
log.setAttribute("isRemote",Boolean.toString(logFile.isRemote()));
log.setAttribute("comments",logFile.getComments());
log.setAttribute("protocol",logFile.getProtocolName());
if (logFile.isRemote())
{
log.setAttribute("uploader",logFile.getRemoteUploader());
log.setAttribute("id",Integer.toString(logFile.getRemoteId()));
}
local.appendChild(log);
}
Element remote = doc.createElement("remote");
root.appendChild(remote);
for (LogFile logFile : this.getRemoteLogs())
{
log = doc.createElement("log");
log.setAttribute("filename",logFile.getName());
log.setAttribute("filesize",Long.toString(logFile.getSize()));
log.setAttribute("comments",logFile.getComments());
log.setAttribute("protocol",logFile.getProtocolName());
if (logFile.isRemote())
{
log.setAttribute("uploader",logFile.getRemoteUploader());
log.setAttribute("id",Integer.toString(logFile.getRemoteId()));
}
remote.appendChild(log);
}
File file = new File(REPOSITORY_FILE);
if (!file.exists())
{
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
DOMSource domSource = new DOMSource(doc);
StreamResult output = new StreamResult(new OutputStreamWriter(fos, "UTF-8"));;
trans.transform(domSource, output);
fos.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}