package models;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
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.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQDataSource;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQResultSequence;
import net.xqj.exist.ExistXQDataSource;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import play.Play;
import se.repos.vfile.VFileDocumentBuilderFactory;
import se.repos.vfile.gen.VFile;
import utils.FileManager;
public class XChroniclerHandler extends BackendHandlerInterface {
/**
* Test folders related
*/
static final String BASE_URL = rootBackendFolder + "XChronicler/";
public static final String REPOSITORY_URL = BASE_URL + "repo/";
/**
* eXist related
*/
protected static String DRIVER = "org.exist.xmldb.DatabaseImpl";
protected static String DBURI = "xmldb:exist://localhost:8080/exist/xmlrpc";
protected static String COLLECTION_PATH = Play.application()
.configuration().getString("eXist.dbPath");
protected static final String DB_USER = Play.application().configuration()
.getString("eXist.user");
protected static final String DB_PASS = Play.application().configuration()
.getString("eXist.pass");
/**
* VFile generation
*/
DocumentBuilder docBuilder = new VFileDocumentBuilderFactory()
.newDocumentBuilder();
private XChroniclerHandler() {
}
/**
* SingletonHolder is loaded on the first execution of
* Singleton.getInstance() or the first access to SingletonHolder.INSTANCE,
* not before.
*/
private static class SingletonHolder {
private static final XChroniclerHandler INSTANCE = new XChroniclerHandler();
}
public static BackendHandlerInterface getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public Object getRepository() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public boolean init() {
System.out.println("running xquery init");
deleteFile(COLLECTION_PATH);
return true;
}
@Override
public String commitAFile(TempFile tf) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public TempFile getFile(String url) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public ArrayList<RepositoryFile> getWorkingDirFiles() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public boolean commit(String url, String content, String message, User user) {
// build temp files
String oldFileContent = "";
try {
oldFileContent = getHeadFile(url);
} catch (XQException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String oldFileUrl = url + ".old";
File oldFile = FileManager.createFile(oldFileContent, oldFileUrl,
XChroniclerHandler.REPOSITORY_URL);
File newFile = FileManager.createFile(content, url,
XChroniclerHandler.REPOSITORY_URL);
String vFileContent = "empty";
ByteArrayOutputStream output = new ByteArrayOutputStream();
// If previous version existing in the database
// Fetches from the database the latest version of the xml
try {
if (exists(url)) {
System.out.println("**********the file exists");
// Updates the vfile
String oldVFileContent = getVFile(url);
String oldVFileUrl = url + ".vold";
File indexFile = FileManager.createFile(oldVFileContent,
oldVFileUrl, XChroniclerHandler.REPOSITORY_URL);
VFile oldVFile = parseVFile(indexFile);
vFileContent = updateVFile(oldVFile, oldFile, newFile);
} else {
System.out.println("**********the file doesnt exist");
// creates the vfile
try {
printDocument(generateVFile(newFile).toDocument(), output);
} catch (IOException | TransformerException e) {
e.printStackTrace();
}
vFileContent = output.toString();
}
} catch (XQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
// String fileName = "url";
// adds the vfile to the database
saveToExist(url, vFileContent);
return true;
}
/**
* simple test that uses the basic files as input
*
* NOTE: requires that the backends/XChronicler/input/basic directory to
* exist and the corresponding files
*
* @return
*/
public String generateVFileSimpleTest() {
String originalPath = BASE_URL + "input/basic/basic_1.xml";
String alteredPath = BASE_URL + "input/basic/basic_2.xml";
VFile vFile = generateVFile(new File(originalPath));
return updateVFile(vFile, originalPath, alteredPath);
}
/**
* Generates V-File based on a single version
*
* Essentially it initializes the versioning for that file
*
* @param initial
* document
* @return
*/
public VFile generateVFile(File initial) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
Document originalDocument;
try {
originalDocument = docBuilder.parse(initial);
String originalTime = "" + System.nanoTime();
String originalVersion = "1";
VFile vFile = VFile.normalizeDocument(originalDocument,
originalTime, originalVersion);
printDocument(vFile.toDocument(), output);
System.out.println("generateVFile output: " + output.toString());
return vFile;
} catch (TransformerException | SAXException | IOException e) {
e.printStackTrace();
return null;
}
}
/**
* parses an existent vFile
*
* @param indexFile
* @return
*/
public VFile parseVFile(File indexFile) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
Document indexDocument = docBuilder.parse(indexFile);
VFile vFile = new VFile(indexDocument);
printDocument(vFile.toDocument(), output);
System.out.println("parseVFile output: " + output.toString());
return vFile;
} catch (SAXException | IOException | TransformerException e) {
e.printStackTrace();
return null;
}
}
/**
* updates V-File based on two different filePaths
*
* @param originalPath
* @param alteredPath
* @return
*/
public String updateVFile(VFile vFile, String originalPath,
String alteredPath) {
return updateVFile(vFile, new File(originalPath), new File(alteredPath));
}
/**
* updates V-File based on two different files
*
* @param vFile
* @param original
* @param altered
* @return
*/
public String updateVFile(VFile vFile, File original, File altered) {
ArrayList<File> files = new ArrayList<File>();
files.add(original);
files.add(altered);
return updateVFileFromArray(vFile, files);
}
/**
* updates V-file based on an array of files
*
* @param vFile
* @param files
* the list with the files
* @return the v-file as a string
*/
public String updateVFileFromArray(VFile vFile, List<File> files) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
Document lastIndexDoc = null;
System.out.println("updating V-file...");
try {
String version = vFile.getDocumentVersion();
System.out.println("version:" + version);
for (int i = 0; i < files.size() - 1; i++) {
System.out.println("parsing file number:" + i);
File originalFile = files.get(i);
File alteredFile = files.get(i + 1);
String newTime = "" + System.nanoTime();
String newVersion = "" + (Integer.parseInt(version)+1);
vFile.update(docBuilder.parse(originalFile),
docBuilder.parse(alteredFile), newTime, newVersion);
lastIndexDoc = vFile.toDocument();
}
printDocument(lastIndexDoc, output);
} catch (TransformerException | SAXException | IOException e) {
e.printStackTrace();
}
return output.toString();
}
/**
* test that picks up a new version of the xml and updates the v file
*
* @deprecated Not working, needs original vfile to be reconverted before
*/
@Deprecated
public void regenerateVFile() {
File originalDocumentFile = new File(BASE_URL + "input/vfile.xml");
File alteredDocumentFile = new File(BASE_URL + "input/basic_3.xml");
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder;
Document originalDocument, alteredDocument;
try {
docBuilder = dbfac.newDocumentBuilder();
originalDocument = docBuilder.parse(originalDocumentFile);
alteredDocument = docBuilder.parse(alteredDocumentFile);
VFile vFile = new VFile(originalDocument);
String newTime = "" + System.nanoTime();
String newVersion = ""
+ Integer.parseInt(vFile.getDocumentVersion()) + 1;
vFile.update(originalDocument, alteredDocument, newTime, newVersion);
Document lastIndexDoc = vFile.toDocument();
printDocument(lastIndexDoc, System.out);
} catch (TransformerException | SAXException
| ParserConfigurationException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void printDocument(Document doc, OutputStream out)
throws IOException, TransformerException {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(doc), new StreamResult(
new OutputStreamWriter(out, "UTF-8")));
}
@Override
public boolean removeExistingRepository() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public String getRepositoryPath() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public RepositoryRevision getRepositoryHEAD() {
String[] files = getFilesFromExist(COLLECTION_PATH);
RepositoryRevision repo = new RepositoryRevision();
for (String file : files) {
RepositoryFile repositoryFile = null;
try {
repositoryFile = new RepositoryFile(file,
getHeadFile(COLLECTION_PATH + file));
} catch (XQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
repo.addRepositoryFile(repositoryFile);
}
return repo;
}
/**
* returns the file requested from the database as a string
*
* @param fileUrl
* @return
*/
public String getVFile(String fileUrl) {
fileUrl = COLLECTION_PATH + fileUrl;
String query = "xquery version '3.0';"
+ "declare namespace v='http://www.repos.se/namespace/v';"
+ "doc('" + fileUrl + "')/*";
try {
return runQuery(query);
} catch (XQException e) {
e.printStackTrace();
return null;
}
}
/**
* Checks if the file exists in the database
*
* @param url
* @return
* @throws XQException
*/
public boolean exists(String url) throws XQException {
System.out.println("exists?" + url);
String output = XChroniclerHandler.getHeadFile(url);
System.out.println("\tget head result:" + output);
return !output.equalsIgnoreCase("");
}
/**
* Saves file to existDB
*
* @param fileUrl
* @param content
* @return
*/
private boolean saveToExist(String fileUrl, String content) {
fileUrl = COLLECTION_PATH + fileUrl;
System.out.println("-----> Saving to Exist: ");
System.out.println("\tFile URL: " + fileUrl);
System.out.println("\tContent:" + content);
URL url;
try {
String putUrl = "http://localhost:8080/exist/rest" + fileUrl;
url = new URL(putUrl);
HttpURLConnection httpCon = (HttpURLConnection) url
.openConnection();
setUserNameAndPass(httpCon);
httpCon.setDoOutput(true);
httpCon.setRequestMethod("PUT");
httpCon.setRequestProperty("content-type",
"application/xml; charset=utf-8");
httpCon.connect();
OutputStreamWriter out = new OutputStreamWriter(
httpCon.getOutputStream());
out.write(content);
out.close();
httpCon.getInputStream();
return true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
private void deleteFile(String fileUrl) {
URL url;
try {
String putUrl = "http://localhost:8080/exist/rest" + fileUrl;
System.out.println(putUrl);
url = new URL(putUrl);
HttpURLConnection httpCon = (HttpURLConnection) url
.openConnection();
setUserNameAndPass(httpCon);
httpCon.setDoOutput(true);
httpCon.setRequestMethod("DELETE");
httpCon.setRequestProperty("content-type",
"application/xml; charset=utf-8");
httpCon.connect();
httpCon.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void setUserNameAndPass(HttpURLConnection con) {
String userPassword = DB_USER + ":" + DB_PASS;
String encoding = new sun.misc.BASE64Encoder().encode(userPassword
.getBytes());
con.setRequestProperty("Authorization", "Basic " + encoding);
}
public static String getHeadFile(String fileUrl) throws XQException {
return checkout("NOW", fileUrl);
}
private String[] getFilesFromExist(String url) {
updateFilelist(url);
String queryAfterNameOfFiles = "for $files in doc('" + url
+ "filelist.xml')//exist:resource"
+ " return string($files/@name)";
String queryReturnString = null;
try {
queryReturnString = runQuery(queryAfterNameOfFiles);
} catch (XQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(queryReturnString);
return queryReturnString.split("\n");
}
private void updateFilelist(String url) {
URL webUrl;
try {
// TODO: Externalize string to the application.conf file
System.out.println("http://localhost:8080/exist/rest/" + url);
webUrl = new URL("http://localhost:8080/exist/rest/" + url);
InputStream is = webUrl.openStream(); // throws an IOException
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
String totalFileList = "";
while ((line = br.readLine()) != null) {
totalFileList += line + "\n";
}
saveToExist("filelist.xml", totalFileList);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static String runQuery(String query) throws XQException {
System.out.println("runQuery: \n\t" + query);
XQDataSource xqs = new ExistXQDataSource();
String returnString = "";
xqs.setProperty("serverName", "localhost");
xqs.setProperty("port", "8080");
XQConnection conn = xqs.getConnection();
XQPreparedExpression xqpe = conn.prepareExpression(query);
XQResultSequence rs = xqpe.executeQuery();
while (rs.next()) {
returnString += rs.getItemAsString(null).replace("xmlns=\"\"", "")
+ "\n";
}
System.out.println("Query result: \n\t" + returnString);
return returnString;
}
@Override
public Logs getLog() {
SortedSet<String> ss = new TreeSet<String>();
String query = "xquery version '3.0';"
+ " declare namespace v='http://www.repos.se/namespace/v';"
+ " for $as in doc('" + COLLECTION_PATH + "')//@v:end"
+ " return string($as)";
try {
ss.addAll(Arrays.asList(runQuery(query).split("\n")));
} catch (XQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
query = "xquery version '3.0';"
+ " declare namespace v='http://www.repos.se/namespace/v';"
+ " for $as in doc('" + COLLECTION_PATH + "')//@v:start"
+ " return string($as)";
try {
ss.addAll(Arrays.asList(runQuery(query).split("\n")));
} catch (XQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Logs logs = new Logs();
for (String version : ss) {
System.out.println(version);
logs.addLog(new Log(version, ""));
}
return logs;
}
/**
* Checkout the repository on a specific revision
*
* TODO: Implement the search of all files in the repository
*
* @param revision
*/
@Override
public RepositoryRevision checkout(String revision) {
String fileUrl = "a.xml";
String fileContent = "";
try {
fileContent = checkout(revision, fileUrl);
} catch (XQException e) {
e.printStackTrace();
}
RepositoryRevision rr = new RepositoryRevision();
rr.addRepositoryFile(new RepositoryFile(fileUrl, fileContent));
return rr;
}
/**
* Checkout a specific revision of a file
*
* @param revision
* @param fileUrl
* relative to {@link COLLECTION_PATH} (ex.: a.xml)
* @return String with the file contents
* @throws XQException
*/
public static String checkout(String revision, String fileUrl)
throws XQException {
String query = "xquery version '3.0';"
+ "declare namespace v = 'http://www.repos.se/namespace/v';"
+ "declare function v:beforeEnd($e, $v)"
+ "{"
+ " if (($e/@v:end) castable as xs:int) then"
+ " (($e/@v:end) > ($v))"
+ " else if (string($e/@v:end) = 'NOW') then"
+ " xs:boolean('1')"
+ " else"
+ " xs:boolean('0')"
+ "};"
+ ""
+ "declare function v:getText($e, $v)"
+ "{"
+ " for $a in $e/v:text"
+ " return"
+ " if ((($a/@v:start) <= ($v)) and (v:beforeEnd($a, $v))) then"
+ " $a/text()"
+ " else"
+ " ()"
+ "};"
+ ""
+ "declare function v:getAttr($e, $v)"
+ "{"
+ " for $a in $e/v:attr"
+ " return"
+ " if ((($a/@v:start) <= ($v)) and (v:beforeEnd($a, $v))) then"
+ " attribute { string($a/@v:name) } { $a }"
+ " else"
+ " ()"
+ "};"
+ ""
+ "declare function v:checkout($e, $v)"
+ "{"
+ " if ((($e/@v:start) <= ($v)) and (v:beforeEnd($e, $v)) and name($e) != 'v:text' and name($e) != 'v:attr') then"
+ " element { name($e) } {"
+ " v:getAttr($e, $v),"
+ " v:getText($e, $v),"
+ " for $child in $e/*"
+ " return v:checkout($child, $v)" + " }"
+ " else" + " ()" + "};" + "" + "v:checkout(doc('"
+ COLLECTION_PATH + fileUrl + "')/v:file/*[*], '" + revision
+ "')" + "";
return runQuery(query);
}
@Override
public HashSet<String> getDiff(int relativeRevisionId) {
// TODO Auto-generated method stub
return null;
}
@Override
public int getVersionId() {
// TODO Auto-generated method stub
return 0;
}
@Override
public boolean revert(int relativeRevision) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean commit(String url, String content, String message,
User user, int relativeVersion) {
// TODO Auto-generated method stub
return false;
}
@Override
public long getSize() {
return 0;
}
}
/*
* public void tryXSLT() { String xsltResource =
* "<?xml version='1.0' encoding='UTF-8'?>\n" +
* "<xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>\n"
* + " <xsl:output method='xml' indent='no'/>\n" + " <xsl:template match='/'>\n"
* +
* " <reRoot><reNode><xsl:value-of select='/root/nodee/@val' /> world</reNode></reRoot>\n"
* + " </xsl:template>\n" + "</xsl:stylesheet>"; String xmlSourceResource =
* "<?xml version='1.0' encoding='UTF-8'?>\n" +
* "<root><node val='hello aa '/><nodee id='hje' val='not hello'/></root>";
*
* StringWriter xmlResultResource = new StringWriter();
*
* Transformer xmlTransformer; try { xmlTransformer =
* TransformerFactory.newInstance().newTransformer( new StreamSource(new
* StringReader(xsltResource)));
*
* xmlTransformer.transform(new StreamSource(new StringReader(
* xmlSourceResource)), new StreamResult(xmlResultResource)); } catch
* (TransformerConfigurationException e) { // TODO Auto-generated catch block
* e.printStackTrace(); } catch (TransformerFactoryConfigurationError e) { //
* TODO Auto-generated catch block e.printStackTrace(); } catch
* (TransformerException e) { // TODO Auto-generated catch block
* e.printStackTrace(); }
*
* System.out.println(xmlResultResource.getBuffer().toString()); }
*/