/*
* Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.registry.synchronization;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.wso2.carbon.registry.core.config.RegistryContext;
import org.wso2.carbon.registry.core.utils.LogWriter;
import org.wso2.carbon.registry.core.utils.RegistryUtils;
import org.wso2.carbon.registry.synchronization.message.Message;
import org.wso2.carbon.registry.synchronization.message.MessageCode;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import java.io.*;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Some utility methods used by the synchronization operations.
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
public class Utils {
/**
* This method writes the meta element to the xml stream up to the children.
*
* @param xmlWriter xml writer
* @param metaElement meta element to write
*
* @throws XMLStreamException if the operation failed
*/
public static void writeMetaElement(XMLStreamWriter xmlWriter, OMElement metaElement)
throws XMLStreamException {
xmlWriter.writeStartElement("resource");
// adding path as an attribute, updated dump has name instead of path
String name = metaElement.getAttributeValue(new QName("name"));
xmlWriter.writeAttribute("name", name);
// adding ignoreConflicts as an attribute, if it is available.:
String ignoreConflicts = metaElement.getAttributeValue(new QName("ignoreConflicts"));
if (ignoreConflicts != null) {
xmlWriter.writeAttribute("ignoreConflicts", ignoreConflicts);
}
// adding isCollection as an attribute
String isCollectionStr = metaElement.getAttributeValue(new QName("isCollection"));
xmlWriter.writeAttribute("isCollection", isCollectionStr);
Iterator childrenIt = metaElement.getChildren();
while (childrenIt.hasNext()) {
Object childObj = childrenIt.next();
if (!(childObj instanceof OMElement)) {
continue;
}
OMElement childElement = (OMElement) childObj;
String childName = childElement.getLocalName();
// the following elements will be serialized to the writer directly
if (childName.equals("mediaType") ||
childName.equals("creator") ||
childName.equals("createdTime") ||
childName.equals("lastUpdater") ||
childName.equals("lastModified") ||
childName.equals("description") ||
childName.equals("properties") ||
childName.equals("comments") ||
childName.equals("taggings") ||
childName.equals("ratings") ||
childName.equals("version") ||
childName.equals("associations")) {
childElement.serialize(xmlWriter);
}
}
}
/**
* This method reads the xml stream up to the children and return the meta element.
*
* @param xmlReader the xml reader.
*
* @return the meta element.
* @throws SynchronizationException if the provided XML is invalid.
* @throws XMLStreamException if XML parsing failed.
*/
public static OMElement readMetaElement(XMLStreamReader xmlReader)
throws SynchronizationException, XMLStreamException {
try {
while (!xmlReader.isStartElement() && xmlReader.hasNext()) {
xmlReader.next();
}
if (!xmlReader.hasNext()) {
// nothing to parse
return null;
}
if (!xmlReader.getLocalName().equals("resource")) {
throw new SynchronizationException(
MessageCode.INVALID_DUMP_CREATE_META_FILE);
}
// alerting non-backward compatibility...
String pathAttribute = xmlReader.getAttributeValue(null, "path");
if (pathAttribute != null) {
throw new SynchronizationException(MessageCode.CHECKOUT_OLD_VERSION);
}
OMFactory factory = OMAbstractFactory.getOMFactory();
OMElement root = factory.createOMElement(new QName("resource"));
String resourceName = xmlReader.getAttributeValue(null, "name");
String isCollectionString = xmlReader.getAttributeValue(null, "isCollection");
root.addAttribute("name", resourceName, null);
root.addAttribute("isCollection", isCollectionString, null);
// traversing to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext());
while (xmlReader.hasNext()) {
String localName = xmlReader.getLocalName();
// version
if (localName.equals("version")) {
String text = xmlReader.getElementText();
OMElement versionElement = factory.createOMElement(new QName("version"));
if (text != null) {
versionElement.setText(text);
}
root.addChild(versionElement);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// setMediaType
else if (localName.equals("mediaType")) {
String text = xmlReader.getElementText();
OMElement mediaTypeElement = factory.createOMElement(new QName("mediaType"));
if (text != null) {
mediaTypeElement.setText(text);
}
root.addChild(mediaTypeElement);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// creator
else if (localName.equals("creator")) {
String text = xmlReader.getElementText();
OMElement creatorElement = factory.createOMElement(new QName("creator"));
if (text != null) {
creatorElement.setText(text);
}
root.addChild(creatorElement);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// createdTime
else if (localName.equals("createdTime")) {
String text = xmlReader.getElementText();
OMElement createdTimeElement =
factory.createOMElement(new QName("createdTime"));
if (text != null) {
createdTimeElement.setText(text);
}
root.addChild(createdTimeElement);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// createdTime
else if (localName.equals("content")) {
// currently we are keeping the content within the root element, and remove it
// later. Before Carbon 3.0.0 the content was in the middle of the other
// resource attributes
String text = xmlReader.getElementText();
OMElement contentElement = factory.createOMElement(new QName("content"));
if (text != null) {
contentElement.setText(text);
}
root.addChild(contentElement);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// setLastUpdater
else if (localName.equals("lastUpdater")) {
String text = xmlReader.getElementText();
OMElement lastUpdaterElement =
factory.createOMElement(new QName("lastUpdater"));
if (text != null) {
lastUpdaterElement.setText(text);
}
root.addChild(lastUpdaterElement);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// LastModified
else if (localName.equals("lastModified")) {
String text = xmlReader.getElementText();
OMElement lastModifiedElement =
factory.createOMElement(new QName("lastModified"));
if (text != null) {
lastModifiedElement.setText(text);
}
root.addChild(lastModifiedElement);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// get description
else if (localName.equals("description")) {
String text = xmlReader.getElementText();
OMElement description = factory.createOMElement(new QName("description"));
if (text != null) {
description.setText(text);
}
root.addChild(description);
// now go to the next element
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
// now go to the next element while
// (!xmlReader.isStartElement() && xmlReader.hasNext());
}
// get properties
else if (localName.equals("properties")) {
// iterating trying to find the children..
OMElement properties = factory.createOMElement(new QName("properties"));
root.addChild(properties);
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
while (xmlReader.hasNext() &&
xmlReader.getLocalName().equals("property")) {
String key = xmlReader.getAttributeValue(null, "key");
String text = xmlReader.getElementText();
OMElement property = factory.createOMElement(new QName("property"));
property.addAttribute("key", key, null);
properties.addChild(property);
if (text.equals("")) {
text = null;
}
if (text != null) {
property.setText(text);
}
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
}
// getting comment information
} else if (localName.equals("comments")) {
// iterating trying to find the children..
OMElement commentsElement = factory.createOMElement(new QName("comments"));
root.addChild(commentsElement);
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext());
while (xmlReader.hasNext() &&
xmlReader.getLocalName().equals("comment")) {
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
localName = xmlReader.getLocalName();
OMElement commentElement = factory.createOMElement(new QName("comment"));
commentsElement.addChild(commentElement);
while (xmlReader.hasNext() &&
(localName.equals("user") || localName.equals("text"))) {
if (localName.equals("user")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement userElement =
factory.createOMElement(new QName("user"));
userElement.setText(text);
commentElement.addChild(userElement);
}
} else if (localName.equals("text")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement textElement =
factory.createOMElement(new QName("text"));
textElement.setText(text);
commentElement.addChild(textElement);
}
}
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
if (xmlReader.hasNext()) {
localName = xmlReader.getLocalName();
}
}
}
}
// getting tagging information
else if (localName.equals("taggings")) {
// iterating trying to find the children..
OMElement taggingsElement = factory.createOMElement(new QName("taggings"));
root.addChild(taggingsElement);
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
while (xmlReader.hasNext() &&
xmlReader.getLocalName().equals("tagging")) {
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
localName = xmlReader.getLocalName();
OMElement taggingElement = factory.createOMElement(new QName("tagging"));
taggingsElement.addChild(taggingElement);
while (xmlReader.hasNext() &&
(localName.equals("user") || localName.equals("date") ||
localName.equals("tagName"))) {
if (localName.equals("user")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement userElement =
factory.createOMElement(new QName("user"));
userElement.setText(text);
taggingElement.addChild(userElement);
}
} else if (localName.equals("date")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement dateElement =
factory.createOMElement(new QName("date"));
dateElement.setText(text);
taggingElement.addChild(dateElement);
}
} else if (localName.equals("tagName")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement tagNameElement =
factory.createOMElement(new QName("tagName"));
tagNameElement.setText(text);
taggingElement.addChild(tagNameElement);
}
}
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
if (xmlReader.hasNext()) {
localName = xmlReader.getLocalName();
}
}
}
}
// getting rating information
else if (localName.equals("ratings")) {
// iterating trying to find the children..
OMElement ratingsElement = factory.createOMElement(new QName("ratings"));
root.addChild(ratingsElement);
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
while (xmlReader.hasNext() &&
xmlReader.getLocalName().equals("rating")) {
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext());
localName = xmlReader.getLocalName();
OMElement ratingElement = factory.createOMElement(new QName("rating"));
ratingsElement.addChild(ratingElement);
while (xmlReader.hasNext() &&
(localName.equals("user") || localName.equals("date") ||
localName.equals("rate"))) {
if (localName.equals("user")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement userElement =
factory.createOMElement(new QName("user"));
userElement.setText(text);
ratingElement.addChild(userElement);
}
} else if (localName.equals("date")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement dateElement =
factory.createOMElement(new QName("date"));
dateElement.setText(text);
ratingElement.addChild(dateElement);
}
} else if (localName.equals("rate")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement rateElement =
factory.createOMElement(new QName("rate"));
rateElement.setText(text);
ratingElement.addChild(rateElement);
}
}
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
if (xmlReader.hasNext()) {
localName = xmlReader.getLocalName();
}
}
}
}
// getting rating information
else if (localName.equals("associations")) {
// iterating trying to find the children..
OMElement associationsElement =
factory.createOMElement(new QName("associations"));
root.addChild(associationsElement);
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
while (xmlReader.hasNext() &&
xmlReader.getLocalName().equals("association")) {
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
localName = xmlReader.getLocalName();
OMElement associationElement =
factory.createOMElement(new QName("association"));
associationsElement.addChild(associationElement);
while (xmlReader.hasNext() &&
(localName.equals("source") || localName.equals("destination") ||
localName.equals("type"))) {
if (localName.equals("source")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement sourceElement =
factory.createOMElement(new QName("source"));
sourceElement.setText(text);
associationElement.addChild(sourceElement);
}
} else if (localName.equals("destination")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement destinationElement =
factory.createOMElement(new QName("destination"));
destinationElement.setText(text);
associationElement.addChild(destinationElement);
}
} else if (localName.equals("type")) {
String text = xmlReader.getElementText();
if (text != null) {
OMElement typeElement =
factory.createOMElement(new QName("type"));
typeElement.setText(text);
associationElement.addChild(typeElement);
}
}
do {
xmlReader.next();
} while (!xmlReader.isStartElement() && xmlReader.hasNext() &&
!(xmlReader.isEndElement() &&
xmlReader.getLocalName().equals("resource")));
if (xmlReader.hasNext()) {
localName = xmlReader.getLocalName();
}
}
}
} else if (localName.equals("children") ||
localName.equals("resource")) {
// checking the children or content element to terminate the check.
break;
} else {
// we do mind having unwanted elements, now go to the next element
break;
}
if ((xmlReader.isEndElement() && xmlReader.getLocalName().equals("resource"))) {
// here we come the end of the resource tag
break;
}
}
return root;
} catch (XMLStreamException e) {
throw new SynchronizationException(
MessageCode.ERROR_IN_READING_STREAM_TO_CREATE_META_FILE);
}
}
/**
* This method creates the file that store the meta data of the current directory or file.
*
* @param fileName the name of the file.
* @param metaData the meta data element.
*
* @throws SynchronizationException if the operation failed.
*/
public static void createMetaFile(String fileName,
OMElement metaData) throws SynchronizationException {
try {
File file = new File(fileName);
String metaDirName = file.getParent();
if (metaDirName != null) {
File metaDir = new File(metaDirName);
if (!metaDir.exists() && !metaDir.mkdir()) {
throw new SynchronizationException(MessageCode.ERROR_CREATING_META_FILE,
new String[]{"file name: " + fileName});
}
}
if (!file.exists() && !file.createNewFile()) {
throw new SynchronizationException(MessageCode.FILE_ALREADY_EXISTS,
new String[]{"file name: " + fileName});
}
FileOutputStream fileOut = null;
try {
fileOut = new FileOutputStream(file, false);
metaData.serialize(fileOut);
} finally {
if (fileOut != null) {
fileOut.close();
}
}
} catch (IOException e) {
throw new SynchronizationException(MessageCode.ERROR_CREATING_META_FILE, e,
new String[]{"file name: " + fileName});
} catch (XMLStreamException e) {
throw new SynchronizationException(MessageCode.ERROR_WRITING_TO_META_FILE, e,
new String[]{"file name: " + fileName});
}
}
/**
* Returns the contents of the file in a byte array.
*
* @param file the file the to read
*
* @return the content of the file
* @throws SynchronizationException if the operation failed.
*/
public static byte[] getBytesFromFile(File file) throws SynchronizationException {
InputStream is = null;
try {
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new SynchronizationException(MessageCode.FILE_TO_READ_IS_NOT_FOUND, e,
new String[]{"file name: " + file.getName()});
}
long length = file.length();
// to ensure that file is not larger than Integer.MAX_VALUE.
if (length > Integer.MAX_VALUE) {
// File is too large
throw new SynchronizationException(MessageCode.FILE_LENGTH_IS_TOO_LARGE,
new String[]{"file name: " + file.getName(),
"file length limit: " + Integer.MAX_VALUE});
}
// byte array to keep the data
byte[] bytes = new byte[(int) length];
int offset = 0;
int numRead;
try {
while (offset < bytes.length
&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
} catch (IOException e) {
throw new SynchronizationException(MessageCode.ERROR_IN_READING, e,
new String[]{"file name: " + file.getName()});
}
if (offset < bytes.length) {
throw new SynchronizationException(MessageCode.ERROR_IN_COMPLETELY_READING,
new String[]{"file name: " + file.getName()});
}
return bytes;
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException ignore) {
// We only want to make sure that the stream is closed. We don't quite need to worry
// whether it failed or not, as this might happen due to another upstream exception.
}
}
}
/**
* Method to obtain the XML representation of the meta information corresponding to the given
* file. This method will first determine the path of the meta file for the given file-path, and
* then obtain the corresponding XML representation using the {@link
* #getOMElementFromMetaFile(String)} method.
*
* @param filePath the path to the file of which the meta information is required.
*
* @return An OM element containing the meta information.
* @throws SynchronizationException if the operation failed.
*/
public static OMElement getMetaOMElement(String filePath) throws SynchronizationException {
File file = new File(filePath);
if (!file.exists()) {
return null;
}
String fileName = file.getName();
String metaFilePath;
if (file.isDirectory()) {
metaFilePath = filePath + File.separator + SynchronizationConstants.META_DIRECTORY +
File.separator +
SynchronizationConstants.META_FILE_PREFIX +
SynchronizationConstants.META_FILE_EXTENSION;
} else {
String parentDirName = file.getParent();
metaFilePath =
parentDirName + File.separator + SynchronizationConstants.META_DIRECTORY +
File.separator + SynchronizationConstants.META_FILE_PREFIX + fileName +
SynchronizationConstants.META_FILE_EXTENSION;
}
// check the existence of the meta directory
File metaFile = new File(metaFilePath);
if (!metaFile.exists()) {
return null;
}
return Utils.getOMElementFromMetaFile(metaFilePath);
}
/**
* Method to obtain the XML representation of the data contained in a meta file.
*
* @param metaFilePath the path to the meta file.
*
* @return An OM element containing the meta information.
* @throws SynchronizationException if the operation failed.
*/
public static OMElement getOMElementFromMetaFile(String metaFilePath) throws
SynchronizationException {
File metaFile = new File(metaFilePath);
OMElement element = null;
// if the file exists, just get it.
FileInputStream directoryMetaFileStream = null;
if (metaFile.exists()) {
try {
directoryMetaFileStream = new FileInputStream(metaFilePath);
//create the parser
XMLStreamReader parser = XMLInputFactory.newInstance()
.createXMLStreamReader(directoryMetaFileStream);
//create the builder
StAXOMBuilder builder = new StAXOMBuilder(parser);
// get the element to restore
element = builder.getDocumentElement().cloneOMElement();
try {
directoryMetaFileStream.close();
} catch (IOException e) {
throw new SynchronizationException(
MessageCode.ERROR_IN_CLOSING_META_FILE_STREAM, e,
new String[]{"meta file name: " + metaFilePath});
}
} catch (IOException e) {
throw new SynchronizationException(MessageCode.ERROR_IN_READING_META_FILE,
e,
new String[]{"meta file name: " + metaFilePath});
} catch (XMLStreamException e) {
throw new SynchronizationException(
MessageCode.ERROR_IN_READING_META_FILE_STREAM,
e,
new String[]{"meta file name: " + metaFilePath});
} finally {
if (directoryMetaFileStream != null) {
try {
directoryMetaFileStream.close();
} catch (IOException ignore) {
// We cannot throw an exception in here because it would swallow the
// incoming exception if there are any. A potential user would in fact be
// interested in the incoming exception instead of this exception.
}
}
}
}
return element;
}
/**
* Method to generate the XML content of a meta file. This method will require information about
* the resource and details of who created it.
*
* @param isCollection whether the resource at the given path is a collection.
* @param path the path of the resource for which the meta information is generated.
* @param username the username of the creator.
*
* @return the meta information as an OM element.
* @throws SynchronizationException if the operation failed.
*/
public static OMElement createDefaultMetaFile(boolean isCollection,
String path,
String username)
throws SynchronizationException {
// if not provide the default details
OMFactory factory = OMAbstractFactory.getOMFactory();
OMElement root = factory.createOMElement(new QName("resource"));
// adding path as an attribute
root.addAttribute("path", path, null);
String resourceName = RegistryUtils.getResourceName(path);
root.addAttribute("name", resourceName, null);
// adding isCollection as an attribute
root.addAttribute("isCollection", isCollection ? "true" : "false", null);
OMElement child;
// guessing the media type by extension
String mediaType = "unknown";
int lastIndex = path.lastIndexOf('.');
if (lastIndex > 0) {
mediaType = path.substring(lastIndex + 1);
}
child = factory.createOMElement(new QName("mediaType"));
child.setText(mediaType);
root.addChild(child);
// set creator
child = factory.createOMElement(new QName("creator"));
child.setText(username);
root.addChild(child);
// set createdTime
long now = System.currentTimeMillis();
child = factory.createOMElement(new QName("createdTime"));
child.setText(Long.toString(now));
root.addChild(child);
// set updator
child = factory.createOMElement(new QName("lastUpdater"));
child.setText(username);
root.addChild(child);
// set LastModified
child = factory.createOMElement(new QName("lastModified"));
child.setText(Long.toString(now));
root.addChild(child);
// set Description
child = factory.createOMElement(new QName("description"));
root.addChild(child);
// create a 0 version tag
child = factory.createOMElement((new QName("version")));
child.setText("0");
root.addChild(child);
return root;
}
/**
* copying the contents of one file to another.
*
* @param source source
* @param destination destination
*
* @throws SynchronizationException throws if the operation failed.
*/
public static void copy(File source, File destination) throws SynchronizationException {
try {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(source);
out = new FileOutputStream(destination);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
try {
if (in != null) {
in.close();
}
} finally {
if (out != null) {
out.close();
}
}
}
} catch (IOException e) {
throw new SynchronizationException(MessageCode.ERROR_IN_COPYING, e,
new String[]{"source: " + source, "target: " + destination});
}
}
/**
* Method to extract the URL of the remote registry instance from the given URL.
*
* @param url aggregate URL containing a concatenation of the registry URL and the resource path
* that is capable of referencing a remote resource. This url will contain only the
* resource path if the resource was local to the given registry instance.
*
* @return the URL of the remote instance, or null if the instance was local.
*/
public static String getRegistryUrl(String url) {
if (url.startsWith("/")) {
// mean this is a local path..
return null;
}
if (url.indexOf("/registry") > 0) {
return url.substring(0, url.lastIndexOf("/registry") + "/registry".length());
}
return null;
}
/**
* Method to extract the resource path from the given URL.
*
* @param url aggregate URL containing a concatenation of the registry URL and the resource path
* that is capable of referencing a remote resource. This url will contain only the
* resource path if the resource was local to the given registry instance.
*
* @return the path of the resource on the registry.
*/
public static String getPath(String url) {
if (url.startsWith("/")) {
// mean this is a local path..
return url;
}
if (url.indexOf("/registry") > 0) {
return url.substring(url.lastIndexOf("/registry") + "/registry".length());
}
return null;
}
/**
* This method will obtain the encoded representation of the given resource name.
*
* @param resourceName the name of the resource.
*
* @return the encoded name.
* @throws SynchronizationException if the operation failed.
* @see URLEncoder
*/
public static String encodeResourceName(String resourceName) throws SynchronizationException {
String encodedName;
try {
encodedName = URLEncoder.encode(resourceName, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new SynchronizationException(MessageCode.ERROR_ENCODING_RESOURCE_NAME, e,
new String[]{"resource name: " + resourceName});
}
return encodedName;
}
/**
* This method will obtain the decoded representation of the given encoded resource path.
*
* @param path the encoded path of the resource.
*
* @return the decoded path.
* @throws SynchronizationException if the operation failed.
* @see URLDecoder
*/
public static String decodeFilename(String path) throws SynchronizationException {
String decodedName;
try {
decodedName = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new SynchronizationException(MessageCode.ERROR_DECODING_PATH, e,
new String[]{"path: " + path});
}
return decodedName;
}
/**
* This method will obtain the consent from the user to delete the specified file and meta file
* (corresponding to the file), if the user agrees to.
*
* @param file the file or directory to delete.
* @param metaFile the meta file corresponding to the file or directory to delete.
* @param callback the callback which is used to obtain the user's consent. If this parameter is
* null, the file and the meta file will be deleted irrespective of the user's
* choice.
*
* @return whether the operation succeeded or not.
* @throws SynchronizationException if an error occurred during the operation.
*/
public static boolean confirmDelete(File file, File metaFile, UserInputCallback callback)
throws SynchronizationException {
String filePath = file.getAbsolutePath();
boolean isDirectory = file.isDirectory();
boolean inputCode;
if (callback == null) {
// The default behaviour is to delete.
inputCode = true;
} else if (file.isDirectory()) {
inputCode = callback.getConfirmation(new Message(
MessageCode.DIRECTORY_DELETE_CONFIRMATION,
new String[]{"file path: " + filePath}),
SynchronizationConstants.DELETE_CONFIRMATION_CONTEXT);
} else {
inputCode = callback.getConfirmation(new Message(
MessageCode.FILE_DELETE_CONFIRMATION,
new String[]{"file path: " + filePath}),
SynchronizationConstants.DELETE_CONFIRMATION_CONTEXT);
}
if (!inputCode) {
// just continue;
return false;
}
// if you come here it is for the permission to delete
boolean deleted = deleteFile(file);
if (!deleted) {
throw new SynchronizationException(MessageCode.ERROR_IN_DELETING,
new String[]{"file path: " + filePath});
}
if (!isDirectory) {
deleted = deleteFile(metaFile);
if (!deleted) {
throw new SynchronizationException(MessageCode.ERROR_IN_DELETING,
new String[]{"file path: " + metaFile.getAbsolutePath()});
}
}
return true;
}
/**
* This method deletes the specified file from the filesystem. If the given file-path
* corresponds to a directory, the directory and everything under it (child files and
* directories) will be recursively deleted in the process.
*
* @param file the file or directory to delete.
*
* @return true if the operation succeeded or false if it failed.
*/
public static boolean deleteFile(File file) {
if (file.isDirectory()) {
String[] children = file.list();
for (String child : children) {
boolean success = deleteFile(new File(file, child));
if (!success) {
return false;
}
}
}
return file.delete();
}
/**
* Method to determine the files that are required to be cleaned up from meta information
* directory and preserve only the given list of files. This method will determine all meta
* files within the given directory that have no corresponding physical files on the filesystem.
* This is used after a check-in operation has been propagated to ensure that the filesystem
* will always remain consistent.
*
* @param directory the meta information directory that requires cleaning up.
* @param filesToPreserve the list of files to preserve.
*
* @return the list of files to be cleaned.
*/
public static List<String> cleanUpDirectory(File directory, List<String> filesToPreserve) {
List<String> filesToClean = new LinkedList<String>();
if (directory.isDirectory()) {
String[] children = directory.list();
for (String child : children) {
File file = new File(directory, child);
String absoluteFilePath = file.getAbsolutePath();
if (!filesToPreserve.contains(absoluteFilePath)) {
filesToClean.add(absoluteFilePath);
}
}
}
return filesToClean;
}
/**
* Method to obtain the MD5 hash value for the given content.
*
* @param content the content as an array of bytes.
*
* @return the MD5 hash of the content.
*/
public static String getMD5(byte[] content) {
MessageDigest m;
try {
m = MessageDigest.getInstance("MD5");
} catch (Exception e) {
//String msg = "Error in generating the md5. ";
//throw new Exception(msg, e);
return null;
}
m.update(content, 0, content.length);
return new BigInteger(1, m.digest()).toString(16);
}
/**
* Determines whether the content of the given file has changed. If the given file is a
* directory, this method will recursively test each file under this directory to determine
* whether the content of any child, or grand child has changed.
* <p/>
* The change in content is determined using MD5 hash values written to the meta files during a
* check-out or update. If the MD5 hash value of the given file was not found in its meta file,
* this operation will assume that a change has been made, irrespective of whether the content
* of the file changed or not.
*
* @param file the file to test for changes.
*
* @return true if the content has changed, or false if not.
* @throws SynchronizationException if an error occurred during the operation.
*/
public static boolean contentChanged(File file)
throws SynchronizationException {
return contentChanged(file, true);
}
// Method that actually checks for content changes. This needs to know whether the call is the
// first-call to this method, or was it a recursive call.
private static boolean contentChanged(File file, boolean isParent)
throws SynchronizationException {
if (!file.exists()) {
// If we don't find a file, this is not a valid checkout.
throw new SynchronizationException(MessageCode.CHECKOUT_BEFORE_CHECK_IN);
} else if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null || files.length == 0) {
// The meta information for this directory has not been found. Therefore, this is
// a new directory.
if (isParent) {
throw new SynchronizationException(MessageCode.CHECKOUT_BEFORE_CHECK_IN);
}
return true;
}
File metaFile = new File(file.getPath() + File.separator +
SynchronizationConstants.META_DIRECTORY);
File[] metaFiles = metaFile.listFiles();
if (metaFiles == null || metaFiles.length == 0) {
// Though the meta directory exists, the meta information for the given directory
// has not been found. Therefore, this is a new directory waiting to be checked in.
return true;
}
// Child directories don't have meta files. Therefore, we need the number of children
// that are not directories.
int childFiles = 0;
for (File child : files) {
if (!child.isDirectory()) {
childFiles++;
}
}
// We don't count the meta file corresponding to this directory.
if ((metaFiles.length - 1) != childFiles) {
// A file has been added or removed from this directory.
return true;
}
for (File child : files) {
// If the given child is not the meta directory, and if the content has changed,
// the content of this directory has changed.
if (!child.getPath().endsWith(SynchronizationConstants.META_DIRECTORY) &&
contentChanged(child, false)) {
return true;
}
}
return false;
}
String parentDirName = file.getParent();
String name = file.getName();
String metaFilePath = parentDirName + File.separator +
SynchronizationConstants.META_DIRECTORY + File.separator +
SynchronizationConstants.META_FILE_PREFIX + name +
SynchronizationConstants.META_FILE_EXTENSION;
String currentMD5 = getMD5(getBytesFromFile(file));
OMElement metaFileElement = getOMElementFromMetaFile(metaFilePath);
String metaFileMD5 = null;
if (metaFileElement != null) {
metaFileMD5 = metaFileElement.getAttributeValue(new QName("md5"));
}
// We obtain the MD5 value of the file and compare it against the one saved in the meta file
// to see whether any change has been done.
return metaFileMD5 == null || !metaFileMD5.equals(currentMD5);
}
/**
* Method to clean the embedded registry instance, after the synchronization operation. This
* method should only be invoked if the synchronization happens at a client that terminates soon
* after the execution of the synchronization operation, to prevent loss of activity logs.
*/
public static void cleanEmbeddedRegistry() {
RegistryContext registryContext = RegistryContext.getBaseInstance();
if (registryContext != null) {
LogWriter logWriter = registryContext.getLogWriter();
if (logWriter != null) {
logWriter.interrupt();
}
}
}
}