package org.openedit.entermedia.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.text.ParseException;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;
import org.openedit.repository.ContentItem;
import org.openedit.repository.filesystem.FileItem;
import org.openedit.util.WindowsUtil;
import com.openedit.OpenEditException;
import com.openedit.page.manage.PageManager;
import com.openedit.util.FileUtils;
import com.openedit.util.XmlUtil;
import com.openedit.util.ZipUtil;
public class SyncFileDownloader
{
public static final String DIR = "dir";
public static final String FILE = "file";
public static final String PATH = "path";
public static final String DATE = "date";
private static final Log log = LogFactory.getLog(SyncFileDownloader.class);
protected HttpClient fieldHttpClient;
protected File fieldRoot;
protected String fieldUsername;
protected String fieldPassword;
protected XmlUtil fieldXmlUtils;
protected ZipUtil fieldZipUtils;
protected WindowsUtil fieldWindowsUtil;
protected FileUtils fieldFileUtils;
protected PageManager fieldPageManager;
protected String fieldLastChecked;
protected String fieldServerUrl; //http://localhost
protected String fieldSyncPath; //depend if this is data or files
protected String fieldLoginPath = "/entermedia/services/rest/login.xml";
protected String fieldDownloadPath = "/entermedia/services/rest/downloadfiles.zip";
public String getDownloadPath()
{
return fieldDownloadPath;
}
public void setDownloadPath(String inDownloadPath)
{
fieldDownloadPath = inDownloadPath;
}
public String getLoginPath()
{
return fieldLoginPath;
}
public void setLoginPath(String inLoginPath)
{
fieldLoginPath = inLoginPath;
}
public String getLastChecked()
{
return fieldLastChecked;
}
public void setLastChecked(String inLastChecked)
{
fieldLastChecked = inLastChecked;
}
public String fieldListXml = "/entermedia/tools/sync/listfiles.xml";
public String getListXml()
{
return fieldListXml;
}
public void setListXml(String inListXml)
{
fieldListXml = inListXml;
}
public Element listRemoteChanges(String inRemotePath)
{
String path = getServerUrl() + getListXml();
PostMethod postMethod = createPostMethod(path);
if( getLastChecked() != null)
{
postMethod.addParameter("since",getLastChecked() );
}
postMethod.addParameter("syncpath", inRemotePath);
// client.getHttpConnectionManager().getParams().setConnectionTimeout(0);
int statusCode1 = executePostMethod(postMethod);
// postMethod.releaseConnection(); //Is this needed?
Element remotelist = xmlElementFromPost(postMethod);
return remotelist;
}
protected int executePostMethod(PostMethod postMethod)
{
int statusCode1 = 0;
String error = null;
try
{
statusCode1 = getHttpClient().executeMethod(postMethod);
if( statusCode1 != 200)
{
error = postMethod.getResponseBodyAsString();
}
}
catch(Exception ex)
{
throw new OpenEditException(ex);
}
if( statusCode1 != 200)
{
throw new OpenEditException(postMethod + " error: " + statusCode1 + " " + error);
}
return statusCode1;
}
protected PostMethod createPostMethod(String inUrl)
{
PostMethod postMethod = new PostMethod(inUrl);
//postMethod.addParameter( "relative", "" + isRelativeSync() );
// String md5 = getCookieEncryption().getPasswordMd5(getPassword());
// String value = getUsername() + "md542" + md5;
// postMethod.addParameter("entermedia.key",value);
// postMethod.addParameter("accountname", getUsername());
// postMethod.addParameter("password", getPassword());
for (Iterator iterator = getZipUtils().getExcludes().iterator(); iterator.hasNext();)
{
String exclude = (String) iterator.next();
postMethod.addParameter("exclude", exclude);
}
return postMethod;
}
protected boolean isDirectory(Element remotefile)
{
return DIR.equalsIgnoreCase(remotefile.getName());
}
protected void unzip(InputStream in) throws IOException
{
File local = getFile(getSyncPath());
// String localPath = resolveLocalPath( element.attributeValue( PATH ) );
// long dateStamp = Long.parseLong( dated ) * 1000;
// File file = getFile( localPath );
//
//
getZipUtils().unzip(in, local, getSyncPath().substring(1));
}
/**
* We reset the time stamps on all downloaded files just to be sure for next
* time
*
* @param inRemote
* @throws ParseException
*/
protected void resetTimeStamps(Map inRemote) throws ParseException
{
for (Iterator iterator = inRemote.values().iterator(); iterator.hasNext();)
{
Element element = (Element) iterator.next();
if (isDirectory(element))
{
continue;
}
String dated = element.attributeValue(DATE);
String localPath = element.attributeValue(PATH);
long dateStamp = Long.parseLong(dated) * 1000; //zero millisecond
File file = getFile(localPath);
if (!file.setLastModified(dateStamp))
{
log.error("Failed to reset timestamp on file: " + file.getAbsolutePath());
}
if (log.isDebugEnabled())
{
log.info("Reset timestamp on " + file.getAbsolutePath());
}
}
}
protected File getFile(String inLocalPath)
{
ContentItem item = getPageManager().getRepository().getStub(inLocalPath);
//This is temporary until we can redo the ZipUtil to only use ContentItems
FileItem file = (FileItem) item;
return file.getFile();
}
public HttpClient getHttpClient()
{
if (fieldHttpClient == null)
{
fieldHttpClient = new HttpClient();
}
return fieldHttpClient;
}
public XmlUtil getXmlUtils()
{
if (fieldXmlUtils == null)
{
fieldXmlUtils = new XmlUtil();
}
return fieldXmlUtils;
}
public ZipUtil getZipUtils()
{
if (fieldZipUtils == null)
{
fieldZipUtils = new ZipUtil();
if (getRoot() == null)
{
throw new OpenEditException("ZipUtil root cannot be null.");
}
fieldZipUtils.setRoot(getRoot());
fieldZipUtils.setPageManager(getPageManager());
}
return fieldZipUtils;
}
protected Element xmlElementFromPost(PostMethod postMethod)
{
try
{
InputStream body = postMethod.getResponseBodyAsStream();
Reader reader = new InputStreamReader(body, "UTF-8");
Element root = getXmlUtils().getXml(reader, "UTF-8");
return root;
}
catch ( Exception ex)
{
throw new OpenEditException(ex);
}
}
public String getSyncPath()
{
return fieldSyncPath;
}
public void setSyncPath(String localPath)
{
fieldSyncPath = localPath;
}
public void addExclude(String inPattern)
{
getZipUtils().addExclude(inPattern);
}
public PageManager getPageManager()
{
return fieldPageManager;
}
public void setPageManager(PageManager inPageManager)
{
fieldPageManager = inPageManager;
}
public FileUtils getFileUtils()
{
if (fieldFileUtils == null)
{
fieldFileUtils = new FileUtils();
}
return fieldFileUtils;
}
public WindowsUtil getWindowsUtil()
{
if (fieldWindowsUtil == null)
{
fieldWindowsUtil = new WindowsUtil();
fieldWindowsUtil.setRoot(getRoot());
}
return fieldWindowsUtil;
}
public String getUsername()
{
return fieldUsername;
}
public void setUsername(String inUsername)
{
fieldUsername = inUsername;
}
public String getPassword()
{
return fieldPassword;
}
public void setPassword(String inPassword)
{
fieldPassword = inPassword;
}
public String getServerUrl()
{
return fieldServerUrl;
}
public void setServerUrl(String url)
{
if (!url.startsWith("http://") && !url.startsWith("https://"))
{
url = "http://" + url;
}
if (url.endsWith("/"))
{
url = url.substring(0,url.length()-1);
}
fieldServerUrl = url;
}
public File getRoot()
{
return fieldRoot;
}
public void setRoot(File inRoot)
{
fieldRoot = inRoot;
}
protected void downloadAllFiles(Map inRemote) throws Exception
{
if ( inRemote.size() == 0 )
{
return;
}
String url = getServerUrl();
// log.info("posting here:" + url);
url += getDownloadPath();
PostMethod postMethod = createPostMethod( url );
//TODO: We should adhere to the URL limit of 255 characters if possible.
// This is working fine server to server, but it may not work if we have
// to pass through a proxy or a firewall.
// Some ideas to get around this:
// 1. Download ~10 files at a time
// 2. Send the directory as an independent parameter so that it is not being
// repeated for every file.
// 3. If we start downloading 10 at a time, we could mutlti-thread the download
// and have this method launch 10 threads downloading 10 at a time.
// 4. We could also send the download file list as an XML file attachment
for ( Iterator iterator = inRemote.values().iterator(); iterator.hasNext(); )
{
Element file = (Element) iterator.next();
String path = file.attributeValue( PATH );
postMethod.addParameter( FILE, path );
}
// Not sure if this is efficient or not
// client.getHttpConnectionManager().getParams().setConnectionTimeout(0);
int statusCode1 = executePostMethod( postMethod );
if ( statusCode1 == 200 )
{
InputStream in = postMethod.getResponseBodyAsStream();
unzip( in );
in.close();
//TODO: find out if this is necessary
resetTimeStamps( inRemote );
}
else
{
log.error( "SyncToServer returned status: " + statusCode1 );
}
}
//This should setup the cookie
public boolean login()
{
PostMethod method = new PostMethod(getServerUrl() + getLoginPath() );
method.addParameter(new NameValuePair("accountname", getUsername()));
method.addParameter(new NameValuePair("password", getPassword()));
int statusCode =0;
try
{
statusCode = getHttpClient().executeMethod(method);
}
catch (HttpException e)
{
throw new OpenEditException(e);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return statusCode == 200;
}
}