From: Mathieu Baudier Date: Thu, 7 Oct 2010 16:33:31 +0000 (+0000) Subject: Introduce Ajaxplorer support in SLC X-Git-Tag: argeo-slc-2.1.7~1141 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=210a203d89ea2e9d60081f7181a5c99181a099bc;p=gpl%2Fargeo-slc.git Introduce Ajaxplorer support in SLC git-svn-id: https://svn.argeo.org/slc/trunk@3829 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/modules/server/org.argeo.slc.webapp.war/META-INF/MANIFEST.MF b/modules/server/org.argeo.slc.webapp.war/META-INF/MANIFEST.MF index d5b0eb9a9..87b8dab4c 100644 --- a/modules/server/org.argeo.slc.webapp.war/META-INF/MANIFEST.MF +++ b/modules/server/org.argeo.slc.webapp.war/META-INF/MANIFEST.MF @@ -26,6 +26,8 @@ Import-Package: javax.jcr;specification-version="1.0.0", org.argeo.slc.msg.event, org.argeo.slc.runtime, org.argeo.slc.services, + org.argeo.slc.web.ajaxplorer.file;specification-version="0.13.0.SNAPSHOT-r3788", + org.argeo.slc.web.ajaxplorer.mvc;specification-version="0.13.0.SNAPSHOT-r3788", org.argeo.slc.web.mvc, org.argeo.slc.web.mvc.controllers, org.argeo.slc.web.mvc.provisioning, diff --git a/modules/server/org.argeo.slc.webapp.war/WEB-INF/ajaxplorer-servlet.xml b/modules/server/org.argeo.slc.webapp.war/WEB-INF/ajaxplorer-servlet.xml new file mode 100644 index 000000000..a706606b2 --- /dev/null +++ b/modules/server/org.argeo.slc.webapp.war/WEB-INF/ajaxplorer-servlet.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + ajxpDriverHandler + + + + + \ No newline at end of file diff --git a/modules/server/org.argeo.slc.webapp.war/WEB-INF/web.xml b/modules/server/org.argeo.slc.webapp.war/WEB-INF/web.xml index e4341a78b..10f237de2 100644 --- a/modules/server/org.argeo.slc.webapp.war/WEB-INF/web.xml +++ b/modules/server/org.argeo.slc.webapp.war/WEB-INF/web.xml @@ -113,6 +113,22 @@ /jcr-manager/* + + + ajaxplorer + org.springframework.web.servlet.DispatcherServlet + + contextClass + org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext + + 1 + + + + ajaxplorer + /ajaxplorer/* + + contextConfigLocation diff --git a/runtime/org.argeo.slc.server/pom.xml b/runtime/org.argeo.slc.server/pom.xml index 4a148d63e..a39981a6d 100644 --- a/runtime/org.argeo.slc.server/pom.xml +++ b/runtime/org.argeo.slc.server/pom.xml @@ -26,6 +26,7 @@ org.argeo.slc.activemq, org.argeo.slc.execution, org.springframework.context.support, + org.tmatesoft.svn.*;resolution:=optional, * @@ -101,5 +102,18 @@ com.springsource.org.antlr + + + com.svnkit + com.springsource.org.tmatesoft.svn + true + + + + diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpAction.java new file mode 100644 index 000000000..23ccc9ce2 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpAction.java @@ -0,0 +1,7 @@ +package org.argeo.slc.web.ajaxplorer; + +import javax.servlet.http.HttpServletRequest; + +public interface AjxpAction{ + public AjxpAnswer execute(T driver, HttpServletRequest request); +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpAnswer.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpAnswer.java new file mode 100644 index 000000000..6c45c335f --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpAnswer.java @@ -0,0 +1,12 @@ +package org.argeo.slc.web.ajaxplorer; + +import javax.servlet.http.HttpServletResponse; + +public interface AjxpAnswer { + public static AjxpAnswer DO_NOTHING = new AjxpAnswer(){ + public void updateResponse(HttpServletResponse response) { + } + }; + + public void updateResponse(HttpServletResponse response); +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpDriver.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpDriver.java new file mode 100644 index 000000000..e96648e6a --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpDriver.java @@ -0,0 +1,7 @@ +package org.argeo.slc.web.ajaxplorer; + +import javax.servlet.http.HttpServletRequest; + +public interface AjxpDriver { + public AjxpAnswer executeAction(HttpServletRequest request); +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpDriverException.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpDriverException.java new file mode 100644 index 000000000..647e2b4f4 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/AjxpDriverException.java @@ -0,0 +1,14 @@ +package org.argeo.slc.web.ajaxplorer; + +public class AjxpDriverException extends RuntimeException { + static final long serialVersionUID = 1l; + + public AjxpDriverException(String arg0) { + super(arg0); + } + + public AjxpDriverException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/SimpleAjxpDriver.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/SimpleAjxpDriver.java new file mode 100644 index 000000000..732b41c82 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/SimpleAjxpDriver.java @@ -0,0 +1,32 @@ +package org.argeo.slc.web.ajaxplorer; + +import java.util.Map; +import java.util.TreeMap; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class SimpleAjxpDriver implements AjxpDriver { + protected final Log log = LogFactory.getLog(getClass()); + private Map> actions = new TreeMap>(); + + public AjxpAnswer executeAction(HttpServletRequest request) { + String actionStr = request.getParameter("get_action"); + if (actionStr == null) { + actionStr = request.getParameter("action"); + } + if (!actions.containsKey(actionStr)) { + throw new AjxpDriverException("Action " + actionStr + + " not defined."); + } + AjxpAction action = actions.get(actionStr); + return action.execute(this,request); + } + + public void setActions(Map> actions) { + this.actions = actions; + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/AbstractFileDownloadAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/AbstractFileDownloadAction.java new file mode 100644 index 000000000..c9a71a1ae --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/AbstractFileDownloadAction.java @@ -0,0 +1,73 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; + +public abstract class AbstractFileDownloadAction extends FileAction { + public AjxpAnswer execute(FileDriver driver, HttpServletRequest request) { + String fileStr = request.getParameter(getFileParameter()); + if (fileStr == null) { + throw new AjxpDriverException( + "A file to download needs to be provided."); + } + File file = new File(driver.getBasePath() + fileStr); + return new AxpBasicDownloadAnswer(file); + } + + /** Return 'file' by default. */ + protected String getFileParameter() { + return "file"; + } + + /** To be overridden. Do nothing by default. */ + protected void setHeaders(HttpServletResponse response, File file) { + // do nothing + } + + protected class AxpBasicDownloadAnswer implements AjxpAnswer { + private final File file; + + public AxpBasicDownloadAnswer(File file) { + this.file = file; + } + + public void updateResponse(HttpServletResponse response) { + InputStream in = null; + OutputStream out = null; + try { + setHeaders(response, file); + + if (log.isDebugEnabled()) + log.debug("Download file " + file); + in = FileUtils.openInputStream(file); + out = response.getOutputStream(); + + copyFile(in, out); + out.flush(); + + } catch (Exception e) { + e.printStackTrace(); + throw new AjxpDriverException("Cannot download file " + file, e); + } finally { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(out); + } + + } + + protected void copyFile(InputStream in, OutputStream out) + throws Exception { + IOUtils.copy(in, out); + } + + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/AjxpFile.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/AjxpFile.java new file mode 100644 index 000000000..22975cb1a --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/AjxpFile.java @@ -0,0 +1,125 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; + +public class AjxpFile { + private final static Log log = LogFactory.getLog(AjxpFile.class); + + // FIXME: more generic modif time format? + private final static SimpleDateFormat sdf = new SimpleDateFormat( + "dd/MM/yyyy hh:mm"); + + private final File file; + private final String parentPath; + private final String filePath; + + private final String ext; + private final FileType type; + + public AjxpFile(File file, String parentPath) { + this.file = file; + this.parentPath = parentPath; + if (parentPath.equals("/")) { + this.filePath = "/" + file.getName(); + } else { + this.filePath = parentPath + "/" + file.getName(); + } + this.ext = file.isDirectory() ? null : file.getName().substring( + file.getName().indexOf('.') + 1); + this.type = FileType.findType(ext); + } + + public String toXml(final LsMode mode, final String encoding) { + try { + StringBuffer buf = new StringBuffer(); + buf.append(""); + + if (log.isTraceEnabled()) + log.trace(buf.toString()); + + return buf.toString(); + } catch (Exception e) { + throw new AjxpDriverException("Could not serialize file " + file, e); + } + } + + private String formatModifTime() { + return sdf.format(new Date(file.lastModified())); + } + + private String formatFileSize() { + return (file.length() / 1024) + " Kb"; + } + + protected void addAttr(String attrName, String attrValue, StringBuffer buf) { + buf.append(" ").append(attrName).append("=\"").append(attrValue) + .append("\""); + } + + /** To be overridden, do nothing by default. */ + protected void addAdditionalAttrs(final StringBuffer buf, + final LsMode mode, final String encoding) { + + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileAction.java new file mode 100644 index 000000000..fc5fdda48 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileAction.java @@ -0,0 +1,9 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.web.ajaxplorer.AjxpAction; + +public abstract class FileAction implements AjxpAction { + protected final Log log = LogFactory.getLog(getClass()); +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDeleteAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDeleteAction.java new file mode 100644 index 000000000..bd8c8f75e --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDeleteAction.java @@ -0,0 +1,39 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; + +public class FileDeleteAction extends FileAction { + + public AjxpAnswer execute(FileDriver driver, HttpServletRequest request) { + Map params = request.getParameterMap(); + for (Object paramKey : params.keySet()) { + String param = paramKey.toString(); + log.debug("param=" + param + " (" + params.get(paramKey)); + if (param.length() < 4) + continue; + else { + + if (param.substring(0, 4).equals("file")) { + String[] values = (String[]) params.get(paramKey); + for (String path : values) { + File file = driver.getFile(path); + executeDelete((T) driver, file); + } + } + } + } + + return AjxpAnswer.DO_NOTHING; + } + + protected void executeDelete(T driver, File file) { + log.debug("Delete file " + file); + file.delete(); + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDownloadAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDownloadAction.java new file mode 100644 index 000000000..b47c91cfc --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDownloadAction.java @@ -0,0 +1,27 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; + +import javax.servlet.http.HttpServletResponse; + +public class FileDownloadAction extends AbstractFileDownloadAction { + + @Override + protected void setHeaders(HttpServletResponse response, File file) { + setDefaultDownloadHeaders(response, file.getName(), file.length()); + } + + public static void setDefaultDownloadHeaders(HttpServletResponse response, + String fileName, Long fileLength) { + response.setContentType("application/force-download; name=\"" + + fileName + "\""); + response.setHeader("Content-Transfer-Encoding", "binary"); + if (fileLength != null) + response.setContentLength(fileLength.intValue()); + response.setHeader("Content-Disposition", "attachement; filename=\"" + + fileName + "\""); + response.setHeader("Expires", "0"); + response.setHeader("Cache-Control", "no-cache, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDriver.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDriver.java new file mode 100644 index 000000000..d38ab3215 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileDriver.java @@ -0,0 +1,40 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; + +import org.argeo.slc.web.ajaxplorer.SimpleAjxpDriver; + +public class FileDriver extends SimpleAjxpDriver{ + private String basePath; + private String encoding = "UTF-8"; + + public String getBasePath() { + return basePath; + } + + public File getBaseDir(){ + return new File(getBasePath()); + } + + public void setBasePath(String basePath) { + if (basePath.charAt(basePath.length() - 1) != File.separatorChar) + basePath = basePath + File.separatorChar; + this.basePath = basePath; + } + + public String getEncoding() { + return encoding; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + public File getFile(String relpath) { + return new File(getBasePath() + relpath).getAbsoluteFile(); + } + + public File getFile(String dir, String fileName) { + return getFile(dir + File.separator + fileName); + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileImageProxyAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileImageProxyAction.java new file mode 100644 index 000000000..1d2d6de45 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileImageProxyAction.java @@ -0,0 +1,17 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; + +import javax.servlet.http.HttpServletResponse; + +public class FileImageProxyAction extends AbstractFileDownloadAction { + + @Override + protected void setHeaders(HttpServletResponse response, File file) { + FileType fileType = FileType.findType(file); + response.setContentType(fileType.getImageType()); + response.setContentLength((int) file.length()); + response.setHeader("Cache-Control", "public"); + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileLsAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileLsAction.java new file mode 100644 index 000000000..4dd49070c --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileLsAction.java @@ -0,0 +1,131 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; +import java.io.FileFilter; +import java.io.OutputStreamWriter; +import java.util.List; +import java.util.Vector; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; + +public class FileLsAction extends + FileAction { + + public AjxpAnswer execute(FileDriver driver, HttpServletRequest request) { + String modeStr = request.getParameter("mode"); + LsMode mode; + if (modeStr == null) + mode = LsMode.NULL; + else if (modeStr.equals("complete")) + mode = LsMode.COMPLETE; + else if (modeStr.equals("file_list")) + mode = LsMode.FILE_LIST; + else if (modeStr.equals("search")) + mode = LsMode.SEARCH; + else + throw new AjxpDriverException("Unkown mode " + modeStr); + + String options = request.getParameter("options"); + if (options != null) { + if ("al".equals(options)) + mode = LsMode.FILE_LIST; + } + + String path = request.getParameter("dir"); + if (path == null) { + path = "/"; + } + + boolean dirOnly = false; + if (mode == LsMode.NULL || mode == LsMode.COMPLETE) { + dirOnly = true; + } + + List ajxpFiles = listFiles((T) driver, path, dirOnly); + /* + * File[] files = dir.listFiles(createFileFilter(request, dir)); + * List ajxpFiles = new Vector(); for (File file : + * files) { if (file.isDirectory()) { ajxpFiles.add(new AjxpFile(file, + * path)); } else { if (!dirOnly) ajxpFiles.add(new AjxpFile(file, + * path)); } } + */ + return new AxpLsAnswer(driver, ajxpFiles, mode); + } + + protected List listFiles(T driver, String path, boolean dirOnly) { + File dir = driver.getFile(path); + + if (!dir.exists()) + throw new AjxpDriverException("Dir " + dir + " does not exist."); + + FileFilter filter = createFileFilter(dir); + File[] files = dir.listFiles(filter); + List ajxpFiles = new Vector(); + for (File file : files) { + if (file.isDirectory()) { + ajxpFiles.add((F) new AjxpFile(file, path)); + } else { + if (!dirOnly) + ajxpFiles.add((F) new AjxpFile(file, path)); + } + } + return ajxpFiles; + } + + /** To be overridden. Accept all by default. */ + protected FileFilter createFileFilter(File dir) { + return new FileFilter() { + public boolean accept(File pathname) { + return true; + } + + }; + } + + protected class AxpLsAnswer implements AjxpAnswer { + private final List files; + private final LsMode mode; + private final FileDriver driver; + + public AxpLsAnswer(FileDriver driver, List files, LsMode mode) { + this.files = files; + this.mode = mode; + this.driver = driver; + } + + public void updateResponse(HttpServletResponse response) { + final String encoding = driver.getEncoding(); + response.setCharacterEncoding(encoding); + response.setContentType("text/xml"); + + ServletOutputStream out = null; + OutputStreamWriter writer = null; + try { + out = response.getOutputStream(); + writer = new OutputStreamWriter(out, encoding); + writer.write(""); + // TODO add current path + writer.write(""); + for (AjxpFile file : files) { + writer.write(file.toXml(mode, encoding)); + } + writer.write(""); + writer.flush(); + + } catch (Exception e) { + throw new AjxpDriverException("Could not write response.", e); + } finally { + IOUtils.closeQuietly(writer); + IOUtils.closeQuietly(out); + } + } + + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileMkdirAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileMkdirAction.java new file mode 100644 index 000000000..5a58372ae --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileMkdirAction.java @@ -0,0 +1,27 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; + +import javax.servlet.http.HttpServletRequest; + +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; + +public class FileMkdirAction extends FileAction { + + public AjxpAnswer execute(FileDriver driver, + HttpServletRequest request) { + String dir = request.getParameter("dir"); + String dirName = request.getParameter("dirname"); + + File newDir = driver.getFile(dir, dirName); + newDir.mkdirs(); + + postProcess((T)driver,newDir); + + return AjxpAnswer.DO_NOTHING; + } + + protected void postProcess(T driver,File newDir) { + + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileType.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileType.java new file mode 100644 index 000000000..bebdc71f1 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileType.java @@ -0,0 +1,70 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; + +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; + +public enum FileType { + FOLDER("folder.png", "Directory"), UNKNOWN("mime_empty.png", "Unkown"), GIF( + "image.png", "GIF Picture"), JPEG("image.png", "JPEG Picture"), PNG( + "image.png", "PNG Picture"); + + private final String icon; + private final String mimeString; + + FileType(String icon, String mimeString) { + this.icon = icon; + this.mimeString = mimeString; + } + + public String getIcon() { + return icon; + } + + public String getMimeString() { + return mimeString; + } + + public boolean isImage() { + return this == GIF || this == JPEG || this == PNG; + } + + public String getImageType() { + switch (this) { + case GIF: + return "image/gif"; + case JPEG: + return "image/jpeg"; + case PNG: + return "image/png"; + } + throw new AjxpDriverException("Image type undefined for " + this); + } + + public static FileType findType(File file) { + String ext = file.isDirectory() ? null : file.getName().substring( + file.getName().indexOf('.') + 1); + return findType(ext); + } + + /** + * Find the type based on the extension. + * + * @param ext + * the extension, null for a directory + */ + public static FileType findType(String extArg) { + if (extArg == null) + return FOLDER; + + String ext = extArg.toLowerCase(); + if (ext.equals("jpg") || ext.equals("jpeg")) + return JPEG; + else if (ext.equals("gif")) + return GIF; + else if (ext.equals("png")) + return PNG; + else + return UNKNOWN; + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileUploadAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileUploadAction.java new file mode 100644 index 000000000..52118f0c6 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/FileUploadAction.java @@ -0,0 +1,52 @@ +package org.argeo.slc.web.ajaxplorer.file; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.io.IOUtils; +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +public class FileUploadAction extends FileAction { + + public AjxpAnswer execute(FileDriver driver, HttpServletRequest request) { + if (!(request instanceof MultipartHttpServletRequest)) { + throw new AjxpDriverException( + "Cann only deal with MultipartHttpServletRequest"); + } + MultipartHttpServletRequest mpr = (MultipartHttpServletRequest) request; + String dir = mpr.getParameter("dir"); + String fileName = mpr.getParameter("Filename"); + + InputStream in = null; + OutputStream out = null; + File file = null; + try { + MultipartFile mpfile = mpr.getFile("Filedata"); + in = mpfile.getInputStream(); + file = driver.getFile(dir, fileName); + out = new FileOutputStream(file); + IOUtils.copy(in, out); + } catch (IOException e) { + throw new AjxpDriverException("Cannot upload file.", e); + } finally { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(out); + } + postProcess((T)driver, file); + + return AjxpAnswer.DO_NOTHING; + } + + protected void postProcess(T driver,File file) { + + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/LsMode.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/LsMode.java new file mode 100644 index 000000000..a17727de8 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/file/LsMode.java @@ -0,0 +1,5 @@ +package org.argeo.slc.web.ajaxplorer.file; + +public enum LsMode { + NULL, COMPLETE, FILE_LIST, SEARCH +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/mvc/AjxpDriverRequestHandler.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/mvc/AjxpDriverRequestHandler.java new file mode 100644 index 000000000..10593fc0d --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/mvc/AjxpDriverRequestHandler.java @@ -0,0 +1,71 @@ +package org.argeo.slc.web.ajaxplorer.mvc; + +import java.io.IOException; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; +import org.argeo.slc.web.ajaxplorer.AjxpDriver; +import org.springframework.web.HttpRequestHandler; + +public class AjxpDriverRequestHandler implements HttpRequestHandler { + + protected final Log log = LogFactory.getLog(getClass()); + private AjxpDriver driver; + + public void handleRequest(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + long id = System.currentTimeMillis(); + try { + if (log.isDebugEnabled()) + logRequest(id, req.getMethod(), req); + + AjxpAnswer answer = driver.executeAction(req); + answer.updateResponse(resp); + + if (log.isTraceEnabled()) + log.trace(id + " " + req.getMethod() + " completed"); + } catch (Exception e) { + log.error(id + " Cannot process request.", e); + throw new ServletException("Cannot process request " + id, e); + } + + } + + protected void logRequest(long id, String method, HttpServletRequest req) { + if (log.isDebugEnabled()) { + StringBuffer buf = new StringBuffer(id + " Received " + method + + ": "); + buf.append('{'); + @SuppressWarnings("unchecked") + Map params = req.getParameterMap(); + int count1 = 0; + for (Map.Entry entry : params.entrySet()) { + if (count1 != 0) + buf.append(", "); + buf.append(entry.getKey()).append("={"); + int count2 = 0; + for (String value : entry.getValue()) { + if (count2 != 0) + buf.append(','); + buf.append(value); + count2++; + } + buf.append('}'); + count1++; + } + buf.append('}'); + log.debug(buf.toString()); + } + } + + public void setDriver(AjxpDriver driver) { + this.driver = driver; + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/mvc/AjxpDriverServlet.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/mvc/AjxpDriverServlet.java new file mode 100644 index 000000000..09e0fb203 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/mvc/AjxpDriverServlet.java @@ -0,0 +1,129 @@ +package org.argeo.slc.web.ajaxplorer.mvc; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Map; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; +import org.argeo.slc.web.ajaxplorer.AjxpDriver; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.beans.BeansException; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.servlet.HttpServletBean; + +public class AjxpDriverServlet extends HttpServletBean { + static final long serialVersionUID = 1l; + + protected final Log log = LogFactory.getLog(getClass()); + private String driverBean; + private AjxpDriver driver; + + @Override + public void init(ServletConfig sc) throws ServletException { + super.init(sc); + WebApplicationContext context = WebApplicationContextUtils + .getRequiredWebApplicationContext(sc.getServletContext()); + driverBean = sc.getInitParameter("driverBean"); + if (driverBean == null) { + throw new ServletException( + "No driver found, please set the driverBean property"); + } + + logger.info("Loading driver " + driverBean); + driver = (AjxpDriver) context.getBean(driverBean); + + // overrideBeanProperties(sc); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + processRequest("GET", req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + processRequest("POST", req, resp); + } + + protected void processRequest(String method, HttpServletRequest req, + HttpServletResponse resp) throws ServletException, IOException { + long id = System.currentTimeMillis(); + try { + if (log.isDebugEnabled()) + logRequest(id, method, req); + + AjxpAnswer answer = driver.executeAction(req); + answer.updateResponse(resp); + + if (log.isDebugEnabled()) + log.debug(id + " " + method + " completed"); + } catch (Exception e) { + log.error(id + " Cannot process request.", e); + throw new ServletException("Cannot process request " + id, e); + } + + } + + public void setDriverBean(String driverName) { + this.driverBean = driverName; + } + + protected void logRequest(long id, String method, HttpServletRequest req) { + if (log.isDebugEnabled()) { + StringBuffer buf = new StringBuffer(id + " Received " + method + + ": "); + buf.append('{'); + Map params = req.getParameterMap(); + int count1 = 0; + for (Map.Entry entry : params.entrySet()) { + if (count1 != 0) + buf.append(", "); + buf.append(entry.getKey()).append("={"); + int count2 = 0; + for (String value : entry.getValue()) { + if (count2 != 0) + buf.append(','); + buf.append(value); + count2++; + } + buf.append('}'); + count1++; + } + buf.append('}'); + log.debug(buf.toString()); + } + } + + protected void overrideBeanProperties(ServletConfig sc) + throws ServletException { + BeanWrapper wrapper = new BeanWrapperImpl(driver); + Enumeration en = sc.getInitParameterNames(); + while (en.hasMoreElements()) { + String name = en.nextElement(); + if (name.indexOf(driverBean + '.') == 0 + && name.length() > (driverBean.length() + 1)) { + String propertyName = name.substring(driverBean.length() + 1); + String value = sc.getInitParameter(name); + if (value != null) { + try { + wrapper.setPropertyValue(propertyName, value); + } catch (BeansException e) { + throw new ServletException("Cannot set property " + + propertyName + " of bean " + driverBean, e); + } + } + } + } + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnAjxpFile.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnAjxpFile.java new file mode 100644 index 000000000..87a2c9dee --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnAjxpFile.java @@ -0,0 +1,23 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import org.argeo.slc.web.ajaxplorer.file.AjxpFile; +import org.argeo.slc.web.ajaxplorer.file.LsMode; +import org.tmatesoft.svn.core.wc.SVNInfo; + +public class SvnAjxpFile extends AjxpFile { + + protected final SVNInfo info; + + public SvnAjxpFile(SVNInfo info, String parentPath) { + super(info.getFile(), parentPath); + this.info = info; + } + + @Override + protected void addAdditionalAttrs(StringBuffer buf, LsMode mode, + String encoding) { + addAttr("author", info.getAuthor(), buf); + addAttr("revision", Long.toString(info.getRevision().getNumber()), buf); + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDeleteAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDeleteAction.java new file mode 100644 index 000000000..7995431f7 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDeleteAction.java @@ -0,0 +1,27 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import java.io.File; + +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.argeo.slc.web.ajaxplorer.file.FileDeleteAction; +import org.tmatesoft.svn.core.SVNException; + +public class SvnDeleteAction extends FileDeleteAction { + @Override + protected void executeDelete(SvnDriver driver, File file) { + try { + driver.beginWriteAction(file.getParentFile()); + + log.debug("SVN Delete: " + file); + driver.getManager().getWCClient().doDelete(file, true, false); + + driver.commitAll("Commit delete of " + file.getName()); + driver.completeWriteAction(file.getParentFile()); + } catch (SVNException e) { + throw new AjxpDriverException("Cannot delete file " + file, e); + } finally { + driver.rollbackWriteAction(file.getParentFile()); + } + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDownloadAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDownloadAction.java new file mode 100644 index 000000000..4700342a4 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDownloadAction.java @@ -0,0 +1,72 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.argeo.slc.web.ajaxplorer.AjxpAction; +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.argeo.slc.web.ajaxplorer.file.FileDownloadAction; +import org.tmatesoft.svn.core.io.SVNRepository; + +public class SvnDownloadAction implements AjxpAction { + + public AjxpAnswer execute(SvnDriver driver, HttpServletRequest request) { + String path = request.getParameter("file"); + if (path.charAt(path.length() - 1) == '/') { + // probably a directory + return AjxpAnswer.DO_NOTHING; + } + + String revStr = request.getParameter("rev"); + Long rev = Long.parseLong(revStr); + return new SvnDownloadAnswer(driver, path, rev); + } + + public class SvnDownloadAnswer implements AjxpAnswer { + private final SvnDriver driver; + private final String path; + private final Long rev; + + public SvnDownloadAnswer(SvnDriver driver, String path, Long rev) { + this.driver = driver; + this.path = path; + this.rev = rev; + } + + public void updateResponse(HttpServletResponse response) { + ServletOutputStream out = null; + try { + FileDownloadAction.setDefaultDownloadHeaders(response, + getFileName(), null); + response.setHeader("AjaXplorer-SvnFileName", getFileName()); + + SVNRepository repository = driver.getRepository(); + out = response.getOutputStream(); + repository.getFile(path, rev, null, out); + } catch (Exception e) { + throw new AjxpDriverException("Cannot download revision " + rev + + " of path " + path, e); + } finally { + IOUtils.closeQuietly(out); + } + } + + protected String getFileName() { + int lastIndexSlash = path.lastIndexOf('/'); + final String origFileName; + if (lastIndexSlash != -1) { + origFileName = path.substring(lastIndexSlash + 1); + } else { + origFileName = path; + } + + int lastIndexPoint = origFileName.lastIndexOf('.'); + String prefix = origFileName.substring(0, lastIndexPoint); + String ext = origFileName.substring(lastIndexPoint); + return prefix + "-" + rev + ext; + } + } +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDriver.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDriver.java new file mode 100644 index 000000000..2d6b3e4d1 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnDriver.java @@ -0,0 +1,249 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import java.io.File; + +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.argeo.slc.web.ajaxplorer.file.FileDriver; +import org.springframework.beans.factory.BeanNameAware; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; +import org.tmatesoft.svn.core.io.SVNRepository; +import org.tmatesoft.svn.core.wc.SVNClientManager; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.admin.SVNAdminClient; + +public class SvnDriver extends FileDriver implements BeanNameAware { + private final static String DEFAULT_DATA_PATH = System + .getProperty("user.home") + + File.separator + "AjaXplorerArchiver" + File.separator + "data"; + + private final static long WRITE_ACTION_TIMEOUT = 10 * 60 * 1000; + + private SVNURL baseUrl; + private SVNClientManager manager; + + private String beanName; + + private boolean isInWriteAction = false; + + public void init() { + FSRepositoryFactory.setup(); + manager = SVNClientManager.newInstance(); + + String basePath = getBasePath(); + if (basePath != null) { + File baseDir = new File(basePath); + if (baseDir.exists()) {// base dir exists + boolean shouldCheckOut = baseDirChecks(baseDir); + if (shouldCheckOut) { + checkOut(baseDir); + } + } else { + checkOut(baseDir); + } + } else { + String defaultBasePath = DEFAULT_DATA_PATH + File.separator + + "svnwc" + File.separator + beanName; + log.warn("No base path provided, use " + defaultBasePath); + setBasePath(defaultBasePath); + + File baseDir = new File(getBasePath()); + if (!baseDir.exists()) { + baseDir.mkdirs(); + } + + if (baseDirChecks(baseDir)) { + if (getBaseUrl() == null) { + String defaultRepoPath = DEFAULT_DATA_PATH + File.separator + + "svnrepos" + File.separator + beanName; + log.warn("No base URL found, create repository at " + + defaultRepoPath); + baseUrl = createRepository(new File(defaultRepoPath)); + } + checkOut(new File(getBasePath())); + } + } + log.info("SVN driver initialized with base url " + getBaseUrl() + + " and base path " + getBasePath()); + } + + /** Builds a SVN URL. */ + public SVNURL getSVNURL(String relativePath) { + try { + return baseUrl.appendPath(relativePath, false); + } catch (SVNException e) { + throw new AjxpDriverException( + "Cannot build URL from relative path " + relativePath + + " and base url " + baseUrl); + } + } + + public SVNRepository getRepository() { + try { + return manager.createRepository(baseUrl, true); + } catch (SVNException e) { + throw new AjxpDriverException("Cannot create repository for " + + baseUrl, e); + } + } + + /** + * Verifies that the provided existing base dir is ok and whether one should + * check out. Set the base url from the working copy. + * + * @return whether one should check out. + */ + protected boolean baseDirChecks(File baseDir) { + if (!baseDir.isDirectory()) { + throw new AjxpDriverException("Base path " + baseDir + + " is not a directory."); + } + + try {// retrieves SVN infos + SVNInfo info = manager.getWCClient().doInfo(baseDir, + SVNRevision.WORKING); + SVNURL baseUrlTemp = info.getURL(); + if (baseUrl != null) { + if (!baseUrl.equals(baseUrlTemp)) { + throw new AjxpDriverException( + "SVN URL of the working copy " + + baseUrlTemp + + " is not compatible with provided baseUrl " + + baseUrl); + } + } else { + this.baseUrl = baseUrlTemp; + } + return false; + } catch (SVNException e) {// no info retrieved + log + .warn("Could not retrieve SVN info from " + + baseDir + + "(" + + e.getMessage() + + "). Guess that it is and empty dir and try to check out from provided URL."); + if (baseDir.listFiles().length != 0) { + throw new AjxpDriverException("Base dir " + baseDir + + " is not a working copy and not an empty dir."); + } + return true; + } + } + + protected void checkOut(File baseDir) { + if (getBaseUrl() == null) { + throw new AjxpDriverException( + "No SVN URL provided, cannot check out."); + } + + // Make sure directory exists + baseDir.mkdirs(); + + try { + long revision = manager.getUpdateClient().doCheckout(getBaseUrl(), + baseDir, SVNRevision.UNDEFINED, SVNRevision.HEAD, true); + log.info("Checked out from " + baseUrl + " to " + baseDir + + " at revision " + revision); + } catch (SVNException e) { + throw new AjxpDriverException("Cannot check out from " + baseUrl + + " to " + baseDir, e); + } + } + + protected SVNURL createRepository(File repoDir) { + try { + SVNAdminClient adminClient = manager.getAdminClient(); + return adminClient.doCreateRepository(repoDir, null, true, false); + } catch (SVNException e) { + throw new AjxpDriverException("Cannot create repository at " + + repoDir, e); + } + } + + private void updateIfRequired(File dir) { + try { + SVNInfo wcInfo = manager.getWCClient().doInfo(getBaseDir(), + SVNRevision.WORKING); + SVNRevision wcRev = wcInfo.getRevision(); + SVNInfo repoInfo = manager.getWCClient().doInfo(getBaseUrl(), + null, SVNRevision.HEAD); + SVNRevision repoRev = repoInfo.getRevision(); + + if (log.isTraceEnabled()) + log + .trace("WC Revision=" + wcRev + ", Repo Revision=" + + repoRev); + + if (!wcRev.equals(repoRev)) { + log.debug("Update working copy from revision " + wcRev + + " to revision " + repoRev); + manager.getUpdateClient().doUpdate(getBaseDir(), + SVNRevision.HEAD, true); + } + } catch (SVNException e) { + throw new AjxpDriverException("Cannot update working copy " + + getBaseDir(),e); + } + } + + public synchronized void beginWriteAction(File dir) { + if (isInWriteAction) { + try { + wait(WRITE_ACTION_TIMEOUT); + } catch (InterruptedException e) { + // silent + } + if (isInWriteAction) { + throw new AjxpDriverException( + "Still in write action after timeout " + + WRITE_ACTION_TIMEOUT + " ms."); + } + } + + isInWriteAction = true; + updateIfRequired(dir); + } + + public synchronized void completeWriteAction(File dir) { + isInWriteAction = false; + notifyAll(); + } + + public synchronized void rollbackWriteAction(File dir) { + // TODO: revert? + isInWriteAction = false; + notifyAll(); + } + + public void commitAll(String message) throws SVNException{ + if(log.isTraceEnabled()) + log.trace("SVN Commit: " + getBaseDir()); + manager.getCommitClient().doCommit(new File[] { getBaseDir() }, true, + message, true, true); + + } + + /** Spring bean name, set at initialization. */ + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public void setBaseUrl(String baseUrl) { + try { + this.baseUrl = SVNURL.parseURIDecoded(baseUrl); + } catch (SVNException e) { + throw new AjxpDriverException("Cannot parse SVN URL " + baseUrl, e); + } + } + + public SVNURL getBaseUrl() { + return baseUrl; + } + + public SVNClientManager getManager() { + return manager; + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnLogAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnLogAction.java new file mode 100644 index 000000000..77b9af04c --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnLogAction.java @@ -0,0 +1,112 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import java.io.File; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.web.ajaxplorer.AjxpAction; +import org.argeo.slc.web.ajaxplorer.AjxpAnswer; +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNLogEntry; +import org.tmatesoft.svn.core.SVNLogEntryPath; +import org.tmatesoft.svn.core.wc.SVNRevision; + +public class SvnLogAction implements AjxpAction { + private final SimpleDateFormat sdfIso = new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + private final Log log = LogFactory.getLog(getClass()); + + public AjxpAnswer execute(SvnDriver driver, HttpServletRequest request) { + String fileStr = request.getParameter("file"); + log.debug("Log file " + fileStr); + if (fileStr == null) { + throw new AjxpDriverException("A file needs to be provided."); + } + File file = new File(driver.getBasePath() + fileStr); + return new SvnLogAnswer(driver, file); + } + + protected class SvnLogAnswer implements AjxpAnswer { + private final SvnDriver driver; + private final File file; + + public SvnLogAnswer(SvnDriver driver, File file) { + this.driver = driver; + this.file = file; + } + + public void updateResponse(HttpServletResponse response) { + PrintWriter writer = null; + try { + writer = response.getWriter(); + writer.append(""); + writer.append(""); + + final List logEntries = new Vector(); + ISVNLogEntryHandler logHandler = new ISVNLogEntryHandler() { + public void handleLogEntry(SVNLogEntry logEntry) + throws SVNException { + logEntries.add(logEntry); + } + }; + + driver.getManager().getLogClient().doLog(new File[] { file }, + SVNRevision.create(0), SVNRevision.HEAD, true, true, + 100, logHandler); + + for (int i = logEntries.size() - 1; i >= 0; i--) { + String xml = logEntryAsXml(logEntries.get(i), file); + if(log.isTraceEnabled()) + log.trace(xml); + writer.append(xml); + } + + writer.append(""); + writer.append(""); + } catch (Exception e) { + throw new AjxpDriverException( + "Cannot retrieve log for " + file, e); + } finally { + IOUtils.closeQuietly(writer); + } + } + + } + + protected String logEntryAsXml(SVNLogEntry entry, File file) { + StringBuffer buf = new StringBuffer(); + buf.append(""); + + buf.append("").append(entry.getAuthor()).append(""); + buf.append("").append(sdfIso.format(entry.getDate())).append( + ""); + + buf.append(""); + Map paths = entry.getChangedPaths(); + for (SVNLogEntryPath path : paths.values()) { + buf.append("").append(path.getPath()).append(""); + } + buf.append(""); + + buf.append("").append(entry.getMessage()).append(""); + + buf.append(""); + return buf.toString(); + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnLsAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnLsAction.java new file mode 100644 index 000000000..5d9dae541 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnLsAction.java @@ -0,0 +1,59 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import java.io.File; +import java.io.FileFilter; +import java.util.List; +import java.util.Vector; + +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.argeo.slc.web.ajaxplorer.file.FileLsAction; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +public class SvnLsAction extends FileLsAction { + + @Override + protected List listFiles(SvnDriver driver, final String path, + final boolean dirOnly) { + try { + File dir = driver.getFile(path); + SVNWCClient client = driver.getManager().getWCClient(); + + final List res = new Vector(); + FileFilter filter = createFileFilter(dir); + File[] files = dir.listFiles(filter); + for (File file : files) { + //SVNStatus status = driver.getManager().getStatusClient().doStatus(file, false); + + SVNInfo info = client.doInfo(file, SVNRevision.WORKING); + if (dirOnly) { + if (file.isDirectory()) + res.add(new SvnAjxpFile(info, path)); + } else { + res.add(new SvnAjxpFile(info, path)); + } + } + return res; + } catch (SVNException e) { + throw new AjxpDriverException("Cannot list svn dir " + path, e); + } + } + + @Override + protected FileFilter createFileFilter(File dir) { + return new FileFilter() { + + public boolean accept(File pathname) { + if (pathname.getName().equals(".svn")) { + return false; + } else { + return true; + } + } + + }; + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnMkdirAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnMkdirAction.java new file mode 100644 index 000000000..32f3b2573 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnMkdirAction.java @@ -0,0 +1,28 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import java.io.File; + +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.argeo.slc.web.ajaxplorer.file.FileMkdirAction; +import org.tmatesoft.svn.core.SVNException; + +public class SvnMkdirAction extends FileMkdirAction { + @Override + protected void postProcess(SvnDriver driver, File newDir) { + try { + driver.beginWriteAction(newDir.getParentFile()); + + log.debug("SVN Add: " + newDir); + driver.getManager().getWCClient().doAdd(newDir, true, + newDir.isDirectory(), true, true); + + driver.commitAll("Commit new dir " + newDir.getName()); + driver.completeWriteAction(newDir.getParentFile()); + } catch (SVNException e) { + throw new AjxpDriverException("Cannot commit new dir" + newDir, e); + } finally { + driver.rollbackWriteAction(newDir.getParentFile()); + } + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnUploadAction.java b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnUploadAction.java new file mode 100644 index 000000000..384fbe553 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/java/org/argeo/slc/web/ajaxplorer/svn/SvnUploadAction.java @@ -0,0 +1,28 @@ +package org.argeo.slc.web.ajaxplorer.svn; + +import java.io.File; + +import org.argeo.slc.web.ajaxplorer.AjxpDriverException; +import org.argeo.slc.web.ajaxplorer.file.FileUploadAction; +import org.tmatesoft.svn.core.SVNException; + +public class SvnUploadAction extends FileUploadAction { + @Override + protected void postProcess(SvnDriver driver, File file) { + try { + driver.beginWriteAction(file.getParentFile()); + + log.debug("SVN Add: " + file); + driver.getManager().getWCClient().doAdd(file, true, + file.isDirectory(), true, true); + + driver.commitAll("Commit file " + file.getName()); + driver.completeWriteAction(file.getParentFile()); + } catch (SVNException e) { + throw new AjxpDriverException("Cannot commit file " + file, e); + } finally { + driver.rollbackWriteAction(file.getParentFile()); + } + } + +} diff --git a/runtime/org.argeo.slc.server/src/main/resources/org/argeo/slc/web/ajaxplorer/file/spring.xml b/runtime/org.argeo.slc.server/src/main/resources/org/argeo/slc/web/ajaxplorer/file/spring.xml new file mode 100644 index 000000000..25a912798 --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/resources/org/argeo/slc/web/ajaxplorer/file/spring.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/runtime/org.argeo.slc.server/src/main/resources/org/argeo/slc/web/ajaxplorer/svn/spring.xml b/runtime/org.argeo.slc.server/src/main/resources/org/argeo/slc/web/ajaxplorer/svn/spring.xml new file mode 100644 index 000000000..10f7f85ae --- /dev/null +++ b/runtime/org.argeo.slc.server/src/main/resources/org/argeo/slc/web/ajaxplorer/svn/spring.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file