From 2845364a1866db1f6e0e249d7df51344c3d35ad0 Mon Sep 17 00:00:00 2001 From: Britt Park Date: Mon, 4 Dec 2006 00:41:06 +0000 Subject: [PATCH] Exported some Repo functionality via RMI. First (still broken) Repo based CLT. Some cleanup and modification to other CLTs. WIP. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4500 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../authentication-services-context.xml | 16 + .../{avm-clt-context.xml => clt-context.xml} | 18 + config/alfresco/core-services-context.xml | 67 +++ .../extension/rmi-services-context.xml | 21 - .../java/org/alfresco/repo/clt/AVMCopyIn.java | 3 - .../org/alfresco/repo/clt/AVMCopyOut.java | 15 +- source/java/org/alfresco/repo/clt/AVMLs.java | 7 +- .../java/org/alfresco/repo/clt/CltBase.java | 39 +- source/java/org/alfresco/repo/clt/RepoLs.java | 60 +++ .../alfresco/repo/remote/RepoRemoteImpl.java | 131 +++++ .../repo/remote/RepoRemoteInputStream.java | 102 ++++ .../repo/remote/RepoRemoteOutputStream.java | 102 ++++ .../repo/remote/RepoRemoteService.java | 281 +++++++++++ .../remote/RepoRemoteTransportService.java | 475 ++++++++++++++++++ .../service/cmr/remote/RepoRemote.java | 98 ++++ .../cmr/remote/RepoRemoteTransport.java | 131 +++++ 16 files changed, 1523 insertions(+), 43 deletions(-) rename config/alfresco/{avm-clt-context.xml => clt-context.xml} (75%) delete mode 100644 config/alfresco/extension/rmi-services-context.xml create mode 100644 source/java/org/alfresco/repo/clt/RepoLs.java create mode 100644 source/java/org/alfresco/repo/remote/RepoRemoteImpl.java create mode 100644 source/java/org/alfresco/repo/remote/RepoRemoteInputStream.java create mode 100644 source/java/org/alfresco/repo/remote/RepoRemoteOutputStream.java create mode 100644 source/java/org/alfresco/repo/remote/RepoRemoteService.java create mode 100644 source/java/org/alfresco/repo/remote/RepoRemoteTransportService.java create mode 100644 source/java/org/alfresco/service/cmr/remote/RepoRemote.java create mode 100644 source/java/org/alfresco/service/cmr/remote/RepoRemoteTransport.java diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index 4fce7dd6db..4c1cce00bb 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -358,4 +358,20 @@ + + + + + + + + org.alfresco.service.cmr.security.AuthenticationService + + + authentication + + + ${avm.remote.port} + + \ No newline at end of file diff --git a/config/alfresco/avm-clt-context.xml b/config/alfresco/clt-context.xml similarity index 75% rename from config/alfresco/avm-clt-context.xml rename to config/alfresco/clt-context.xml index 8c939fa361..72b9b44a0e 100644 --- a/config/alfresco/avm-clt-context.xml +++ b/config/alfresco/clt-context.xml @@ -50,4 +50,22 @@ true + + + + rmi://localhost:1313/repo + + + org.alfresco.service.cmr.remote.RepoRemoteTransport + + + true + + + + + + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 0da1472273..aa3e26d875 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -740,4 +740,71 @@ + + + + + + + + + + + + + + + + + org.alfresco.service.cmr.remote.RepoRemote + + + + + + + + + + ${server.transaction.mode.default} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.default} + ${server.transaction.mode.default} + ${server.transaction.mode.default} + + + + + + + + + + + + + + 30000 + + + + + + + + + + org.alfresco.service.cmr.remote.RepoRemoteTransport + + + repo + + + ${avm.remote.port} + + + diff --git a/config/alfresco/extension/rmi-services-context.xml b/config/alfresco/extension/rmi-services-context.xml deleted file mode 100644 index ef26f351d9..0000000000 --- a/config/alfresco/extension/rmi-services-context.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - org.alfresco.service.cmr.security.AuthenticationService - - - authentication - - - ${avm.remote.port} - - - - diff --git a/source/java/org/alfresco/repo/clt/AVMCopyIn.java b/source/java/org/alfresco/repo/clt/AVMCopyIn.java index 72bebf71e7..2ca4fd9dc1 100644 --- a/source/java/org/alfresco/repo/clt/AVMCopyIn.java +++ b/source/java/org/alfresco/repo/clt/AVMCopyIn.java @@ -145,9 +145,6 @@ public class AVMCopyIn extends CltBase } } - /** - * @param args - */ public static void main(String[] args) { AVMCopyIn me = new AVMCopyIn(); diff --git a/source/java/org/alfresco/repo/clt/AVMCopyOut.java b/source/java/org/alfresco/repo/clt/AVMCopyOut.java index 81254a74ea..ac5087d6e4 100644 --- a/source/java/org/alfresco/repo/clt/AVMCopyOut.java +++ b/source/java/org/alfresco/repo/clt/AVMCopyOut.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.util.Pair; /** * Copy out a file or a directory recursively from the repository @@ -40,13 +41,9 @@ public class AVMCopyOut extends CltBase { fVerbose = false; } - String [] versionPath = args.get(0).split("@"); - if (versionPath.length != 2) - { - usage(USAGE); - } - String path = versionPath[0]; - int version = Integer.parseInt(versionPath[1]); + Pair versionPath = splitPathVersion(args.get(0)); + String path = versionPath.getFirst(); + int version = versionPath.getSecond(); AVMNodeDescriptor desc = fAVMRemote.lookup(version, path); if (flags.containsKey("-r")) { @@ -55,13 +52,13 @@ public class AVMCopyOut extends CltBase } if (desc == null) { - System.err.println(versionPath[0] + " does not exist."); + System.err.println(path + " does not exist."); fContext.close(); System.exit(1); } if (!desc.isFile()) { - System.err.println(versionPath[0] + " is not a file."); + System.err.println(path + " is not a file."); fContext.close(); System.exit(1); } diff --git a/source/java/org/alfresco/repo/clt/AVMLs.java b/source/java/org/alfresco/repo/clt/AVMLs.java index 31fec158c5..6ec130a53d 100644 --- a/source/java/org/alfresco/repo/clt/AVMLs.java +++ b/source/java/org/alfresco/repo/clt/AVMLs.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.util.Pair; /** * Get a listing of a node. @@ -24,9 +25,9 @@ public class AVMLs extends CltBase @Override protected void run(Map> flags, List args) { - String[] pathVersion = args.get(0).split("@"); - AVMNodeDescriptor desc = fAVMRemote.lookup(Integer.parseInt(pathVersion[1]), - pathVersion[0]); + Pair pathVersion = splitPathVersion(args.get(0)); + AVMNodeDescriptor desc = fAVMRemote.lookup(pathVersion.getSecond(), + pathVersion.getFirst()); if (flags.containsKey("-R")) { recursiveList(desc, 0); diff --git a/source/java/org/alfresco/repo/clt/CltBase.java b/source/java/org/alfresco/repo/clt/CltBase.java index f9659ba6fc..626dcae995 100644 --- a/source/java/org/alfresco/repo/clt/CltBase.java +++ b/source/java/org/alfresco/repo/clt/CltBase.java @@ -14,7 +14,9 @@ import java.util.Map; import org.alfresco.repo.remote.ClientTicketHolder; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.remote.AVMRemote; +import org.alfresco.service.cmr.remote.RepoRemote; import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.util.Pair; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -33,6 +35,11 @@ public abstract class CltBase * The instance of the remote sync service interface. */ protected AVMSyncService fAVMSyncService; + + /** + * The instance of the remote repo interface. + */ + protected RepoRemote fRepoRemote; /** * The ApplicationContext. @@ -44,6 +51,11 @@ public abstract class CltBase */ protected AuthenticationService fAuthenticationService; + /** + * The usage string. + */ + private String fUsage; + /** * Construct a new one. This takes care of instantiating * the application context and grabs references to the @@ -52,9 +64,10 @@ public abstract class CltBase */ protected CltBase() { - fContext = new ClassPathXmlApplicationContext("alfresco/avm-clt-context.xml"); + fContext = new ClassPathXmlApplicationContext("alfresco/clt-context.xml"); fAVMRemote = (AVMRemote)fContext.getBean("avmRemote"); fAVMSyncService = (AVMSyncService)fContext.getBean("avmSyncService"); + fRepoRemote = (RepoRemote)fContext.getBean("repoRemote"); fAuthenticationService = (AuthenticationService)fContext.getBean("authenticationService"); fAuthenticationService.authenticate("admin", "admin".toCharArray()); String ticket = fAuthenticationService.getCurrentTicket(); @@ -77,6 +90,7 @@ public abstract class CltBase int minArgs, String usageMessage) { + fUsage = usageMessage; Map flagArgs = new HashMap(); Map> flagValues = new HashMap>(); List actualArgs = new ArrayList(); @@ -91,7 +105,7 @@ public abstract class CltBase { if (args[pos].equals("-h")) { - usage(usageMessage); + usage(); } // If the argument is one of the accepted flags then it's // a flag. @@ -103,7 +117,7 @@ public abstract class CltBase // Check for too few arguments if (args.length - pos < count) { - usage(usageMessage); + usage(); } // Stuff the parsed flag away. List flArgs = new ArrayList(); @@ -122,7 +136,7 @@ public abstract class CltBase // Check for too few arguments. if (actualArgs.size() < minArgs) { - usage(usageMessage); + usage(); } // Do the work. run(flagValues, actualArgs); @@ -132,11 +146,10 @@ public abstract class CltBase /** * Handle syntax error by exiting. - * @param usageMessage The message to print. */ - protected void usage(String usageMessage) + protected void usage() { - System.err.println(usageMessage); + System.err.println(fUsage); fContext.close(); System.exit(1); } @@ -197,5 +210,17 @@ public abstract class CltBase } } + protected Pair splitPathVersion(String pathVersion) + { + int index = pathVersion.lastIndexOf('@'); + if (index == -1) + { + usage(); + } + String path = pathVersion.substring(0, index); + int version = Integer.parseInt(pathVersion.substring(index + 1)); + return new Pair(path, version); + } + protected abstract void run(Map> flags, List args); } diff --git a/source/java/org/alfresco/repo/clt/RepoLs.java b/source/java/org/alfresco/repo/clt/RepoLs.java new file mode 100644 index 0000000000..9ced5261b8 --- /dev/null +++ b/source/java/org/alfresco/repo/clt/RepoLs.java @@ -0,0 +1,60 @@ +/** + * + */ +package org.alfresco.repo.clt; + +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * List the contents of a directory in a repo. + * @author britt + */ +public class RepoLs extends CltBase +{ + private static Object [] flagDefs = { "-R", 0 }; + + private static String USAGE = "usage: RepoLs path"; + + /* (non-Javadoc) + * @see org.alfresco.repo.clt.CltBase#run(java.util.Map, java.util.List) + */ + @Override + protected void run(Map> flags, List args) + { + NodeRef root = fRepoRemote.getRoot(); + NodeRef dir = null; + String path = args.get(0); + if (path.equals("/")) + { + dir = root; + } + else + { + while (path.startsWith("/")) + { + path = path.substring(1); + } + dir = fRepoRemote.lookup(root, path); + if (dir == null) + { + System.err.println(path + " does not exist"); + fContext.close(); + System.exit(1); + } + } + Map listing = fRepoRemote.getListing(dir); + for (String name : listing.keySet()) + { + System.out.println(name); + } + } + + public static void main(String[] args) + { + RepoLs me = new RepoLs(); + me.exec(args, flagDefs, 1, USAGE); + } +} diff --git a/source/java/org/alfresco/repo/remote/RepoRemoteImpl.java b/source/java/org/alfresco/repo/remote/RepoRemoteImpl.java new file mode 100644 index 0000000000..ecbce39259 --- /dev/null +++ b/source/java/org/alfresco/repo/remote/RepoRemoteImpl.java @@ -0,0 +1,131 @@ +/** + * + */ +package org.alfresco.repo.remote; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + +import org.alfresco.service.cmr.remote.RepoRemote; +import org.alfresco.service.cmr.remote.RepoRemoteTransport; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Client side implementation of RepoRemote interface. + * @author britt + */ +public class RepoRemoteImpl implements RepoRemote +{ + /** + * The underlying remote transport. + */ + private RepoRemoteTransport fTransport; + + /** + * Default constructor. + */ + public RepoRemoteImpl() + { + } + + /** + * Set the transport instance. + */ + public void setRepoRemoteTransport(RepoRemoteTransport transport) + { + fTransport = transport; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#createDirectory(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef createDirectory(NodeRef base, String path) + { + return fTransport.createDirectory(ClientTicketHolder.GetTicket(), base, path); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#createFile(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public OutputStream createFile(NodeRef base, String path) + { + return new RepoRemoteOutputStream(fTransport.createFile(ClientTicketHolder.GetTicket(), base, path), + fTransport); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#getListing(org.alfresco.service.cmr.repository.NodeRef) + */ + public Map getListing(NodeRef dir) + { + return fTransport.getListing(ClientTicketHolder.GetTicket(), dir); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#getRoot() + */ + public NodeRef getRoot() + { + return fTransport.getRoot(ClientTicketHolder.GetTicket()); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#lookup(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef lookup(NodeRef base, String path) + { + return fTransport.lookup(ClientTicketHolder.GetTicket(), base, path); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#readFile(org.alfresco.service.cmr.repository.NodeRef) + */ + public InputStream readFile(NodeRef fileRef) + { + return new RepoRemoteInputStream(fTransport.readFile(ClientTicketHolder.GetTicket(), fileRef), + fTransport); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#readFile(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public InputStream readFile(NodeRef base, String path) + { + return new RepoRemoteInputStream(fTransport.readFile(ClientTicketHolder.GetTicket(), base, path), + fTransport); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#removeNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void removeNode(NodeRef toRemove) + { + fTransport.removeNode(ClientTicketHolder.GetTicket(), toRemove); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#removeNode(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public void removeNode(NodeRef base, String path) + { + fTransport.removeNode(ClientTicketHolder.GetTicket(), base, path); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#rename(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) + */ + public void rename(NodeRef base, String src, String dst) + { + fTransport.rename(ClientTicketHolder.GetTicket(), base, src, dst); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#writeFile(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public OutputStream writeFile(NodeRef base, String path) + { + return new RepoRemoteOutputStream(fTransport.writeFile(ClientTicketHolder.GetTicket(), base, path), + fTransport); + } +} diff --git a/source/java/org/alfresco/repo/remote/RepoRemoteInputStream.java b/source/java/org/alfresco/repo/remote/RepoRemoteInputStream.java new file mode 100644 index 0000000000..1b9f576bc7 --- /dev/null +++ b/source/java/org/alfresco/repo/remote/RepoRemoteInputStream.java @@ -0,0 +1,102 @@ +/** + * + */ +package org.alfresco.repo.remote; + +import java.io.IOException; +import java.io.InputStream; + +import org.alfresco.service.cmr.remote.RepoRemoteTransport; + +/** + * A wrapper implementation of InputStream to work with + * a RepoRemoteTransport instance. + * @author britt + */ +public class RepoRemoteInputStream extends InputStream +{ + /** + * The RepoRemoteTransport reference. + */ + private RepoRemoteTransport fRepoRemote; + + /** + * The handle to the input stream. + */ + private String fHandle; + + /** + * Construct one. + * @param handle The handle returned by getInputStream(); + * @param remote The AVMRemote instance. + */ + public RepoRemoteInputStream(String handle, RepoRemoteTransport remote) + { + fHandle = handle; + fRepoRemote = remote; + } + + /** + * Read in a single byte. + * @return The byte as 0-255 or -1 for eof. + */ + @Override + public int read() throws IOException + { + try + { + byte [] buff = fRepoRemote.readInput(ClientTicketHolder.GetTicket(), fHandle, 1); + if (buff.length == 0) + { + return -1; + } + return ((int)buff[0]) & 0xff; + } + catch (Exception e) + { + throw new IOException("Remote I/O Error."); + } + } + + /** + * Read a buffer of bytes. + * @param b The buffer into which to put the bytes. + * @param off The offset into the buffer. + * @param len The number of bytes to read. + * @return The actual number of bytes read or -1 on eof. + */ + @Override + public int read(byte[] b, int off, int len) throws IOException + { + try + { + byte [] buff = fRepoRemote.readInput(ClientTicketHolder.GetTicket(), fHandle, len); + if (buff.length == 0) + { + return -1; + } + System.arraycopy(buff, 0, b, off, buff.length); + return buff.length; + } + catch (Exception e) + { + throw new IOException("Remote I/O Error."); + } + } + + /** + * Close the underlying AVMRemote handle. + */ + @Override + public void close() throws IOException + { + try + { + fRepoRemote.closeInputHandle(ClientTicketHolder.GetTicket(), fHandle); + } + catch (Exception e) + { + throw new IOException("Remote Error closing input stream."); + } + } +} diff --git a/source/java/org/alfresco/repo/remote/RepoRemoteOutputStream.java b/source/java/org/alfresco/repo/remote/RepoRemoteOutputStream.java new file mode 100644 index 0000000000..8a63ceb716 --- /dev/null +++ b/source/java/org/alfresco/repo/remote/RepoRemoteOutputStream.java @@ -0,0 +1,102 @@ +/** + * + */ +package org.alfresco.repo.remote; + +import java.io.IOException; +import java.io.OutputStream; + +import org.alfresco.service.cmr.remote.RepoRemoteTransport; + +/** + * A wrapper implementation of OutputStream to work with a + * RepoRemoteTransport instance. + * @author britt + */ +public class RepoRemoteOutputStream extends OutputStream +{ + private RepoRemoteTransport fRepoRemote; + + private String fHandle; + + /** + * Create a new one. + * @param handle The handle returned from an RepoRemoteTransport call. + * @param remote The AVMRemote instance. + */ + public RepoRemoteOutputStream(String handle, RepoRemoteTransport remote) + { + fRepoRemote = remote; + fHandle = handle; + } + + /** + * Write one character. + * @param b The character. + */ + @Override + public void write(int b) + throws IOException + { + byte [] buff = new byte[1]; + buff[0] = (byte)b; + write(buff); + } + + /** + * Close the stream. + */ + @Override + public void close() + throws IOException + { + try + { + fRepoRemote.closeOutputHandle(ClientTicketHolder.GetTicket(), fHandle); + } + catch (Exception e) + { + throw new IOException("IO Error: " + e); + } + } + + /** + * Write a portion of a block of bytes. + * @param b The buffer containing the data. + * @param off The offset into the buffer. + * @param len The number of bytes to write. + */ + @Override + public void write(byte[] b, int off, int len) + throws IOException + { + try + { + if (off == 0) + { + fRepoRemote.writeOutput(ClientTicketHolder.GetTicket(), fHandle, b, len); + } + else + { + byte [] buff = new byte[len]; + System.arraycopy(b, off, buff, 0, len); + fRepoRemote.writeOutput(ClientTicketHolder.GetTicket(), fHandle, buff, len); + } + } + catch (Exception e) + { + throw new IOException("IO Error: " + e); + } + } + + /** + * Write a buffer of data. + * @param b The buffer. + */ + @Override + public void write(byte[] b) + throws IOException + { + write(b, 0, b.length); + } +} diff --git a/source/java/org/alfresco/repo/remote/RepoRemoteService.java b/source/java/org/alfresco/repo/remote/RepoRemoteService.java new file mode 100644 index 0000000000..7bc4e7e954 --- /dev/null +++ b/source/java/org/alfresco/repo/remote/RepoRemoteService.java @@ -0,0 +1,281 @@ +/** + * + */ +package org.alfresco.repo.remote; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.remote.RepoRemote; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.util.Pair; +import org.apache.log4j.Logger; + +/** + * Server side implementation of RepoRemote. + * @author britt + */ +public class RepoRemoteService implements RepoRemote +{ + private static Logger fgLogger = Logger.getLogger(RepoRemoteService.class); + + /** + * The NodeService instance. + */ + private NodeService fNodeService; + + /** + * The ContentService instance. + */ + private ContentService fContentService; + + /** + * The FileFolderService instance. + */ + private FileFolderService fFileFolderService; + + /** + * Default constructor. + */ + public RepoRemoteService() + { + } + + /** + * Set the NodeService instance. + */ + public void setNodeService(NodeService service) + { + fNodeService = service; + } + + /** + * Set the ContentService instance. + */ + public void setContentService(ContentService service) + { + fContentService = service; + } + + /** + * Set the FileFolderService instance. + */ + public void setFileFolderService(FileFolderService service) + { + fFileFolderService = service; + } + + /** + * Path splitting utility. + * @param path The path. + * @return A List of components. + */ + private List splitPath(String path) + { + String [] pathComponents = path.split("/+"); + List pathList = new ArrayList(pathComponents.length); + for (String comp : pathComponents) + { + pathList.add(comp); + } + return pathList; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#createDirectory(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef createDirectory(NodeRef base, String path) + { + Pair parentChild = getParentChildRelative(base, path); + FileInfo created = fFileFolderService.create(parentChild.getFirst(), + parentChild.getSecond(), + ContentModel.TYPE_FOLDER); + return created.getNodeRef(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#createFile(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public OutputStream createFile(NodeRef base, String path) + { + Pair parentChild = getParentChildRelative(base, path); + FileInfo info = fFileFolderService.create(parentChild.getFirst(), + parentChild.getSecond(), + ContentModel.TYPE_CONTENT); + // TODO is update supposed to be true. + ContentWriter writer = fContentService.getWriter(info.getNodeRef(), ContentModel.PROP_CONTENT, true); + return writer.getContentOutputStream(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#getListing(org.alfresco.service.cmr.repository.NodeRef) + */ + public Map getListing(NodeRef dir) + { + List list = fFileFolderService.listFiles(dir); + Map result = new TreeMap(); + for (FileInfo info : list) + { + result.put(info.getName(), info.getNodeRef()); + } + return result; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#getRoot() + */ + public NodeRef getRoot() + { + NodeRef storeRoot = fNodeService.getRootNode(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore")); + List listing = fNodeService.getChildAssocs(storeRoot); + for (ChildAssociationRef child : listing) + { + fgLogger.error(child.getQName().getLocalName()); + if (child.getQName().getLocalName().equals("company_home")) + { + return child.getChildRef(); + } + } + throw new AlfrescoRuntimeException("Root Not Found!"); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#lookup(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef lookup(NodeRef base, String path) + { + List pathList = splitPath(path); + if (pathList.size() == 1) + { + return fNodeService.getChildByName(base, ContentModel.ASSOC_CONTAINS, pathList.get(0)); + } + try + { + FileInfo info = fFileFolderService.resolveNamePath(base, pathList); + return info.getNodeRef(); + } + catch (FileNotFoundException e) + { + return null; + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#readFile(org.alfresco.service.cmr.repository.NodeRef) + */ + public InputStream readFile(NodeRef fileRef) + { + return fContentService.getReader(fileRef, ContentModel.PROP_CONTENT).getContentInputStream(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#readFile(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public InputStream readFile(NodeRef base, String path) + { + NodeRef fileRef = lookup(base, path); + if (fileRef == null) + { + throw new AlfrescoRuntimeException("Not Found: " + path); + } + return fContentService.getReader(fileRef, ContentModel.PROP_CONTENT).getContentInputStream(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#removeNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void removeNode(NodeRef toRemove) + { + fNodeService.deleteNode(toRemove); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#removeNode(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public void removeNode(NodeRef base, String path) + { + NodeRef toRemove = lookup(base, path); + if (toRemove == null) + { + throw new AlfrescoRuntimeException("Not Found: " + path); + } + fNodeService.deleteNode(toRemove); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#rename(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) + */ + public void rename(NodeRef base, String src, String dst) + { + NodeRef srcRef = lookup(base, src); + if (srcRef == null) + { + throw new AlfrescoRuntimeException("Not Found: " + src); + } + Pair parentChild = getParentChildRelative(base, dst); + try + { + fFileFolderService.move(srcRef, parentChild.getFirst(), parentChild.getSecond()); + } + catch (FileNotFoundException e) + { + throw new AlfrescoRuntimeException("Parent Not Found: " + dst, e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemote#writeFile(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public OutputStream writeFile(NodeRef base, String path) + { + NodeRef target = lookup(base, path); + return fContentService.getWriter(target, ContentModel.PROP_CONTENT, true).getContentOutputStream(); + } + + /** + * Utility for getting the parent NodeRef of a relative path. + * @param base The base node ref. + * @param path The relative path. + * @return A Pair with the parent node ref and the name of the child. + */ + private Pair getParentChildRelative(NodeRef base, String path) + { + List pathList = splitPath(path); + NodeRef parent; + String name = null; + if (pathList.size() == 1) + { + parent = base; + name = pathList.get(0); + } + else + { + try + { + name = pathList.get(pathList.size() - 1); + pathList.remove(pathList.size() - 1); + FileInfo info = fFileFolderService.resolveNamePath(base, pathList); + parent = info.getNodeRef(); + } + catch (FileNotFoundException e) + { + throw new AlfrescoRuntimeException("Not Found: " + pathList, e); + } + } + return new Pair(parent, name); + } +} diff --git a/source/java/org/alfresco/repo/remote/RepoRemoteTransportService.java b/source/java/org/alfresco/repo/remote/RepoRemoteTransportService.java new file mode 100644 index 0000000000..d28d7f909f --- /dev/null +++ b/source/java/org/alfresco/repo/remote/RepoRemoteTransportService.java @@ -0,0 +1,475 @@ +/** + * + */ +package org.alfresco.repo.remote; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.remote.RepoRemote; +import org.alfresco.service.cmr.remote.RepoRemoteTransport; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.util.GUID; + +/** + * The server side implementation of RepoRemoteTransport. It + * handles ticket validation, and expiration of idle streams. + * @author britt + */ +public class RepoRemoteTransportService implements RepoRemoteTransport, + Runnable +{ + /** + * The map of handles to open input streams. + */ + private Map fInputStreams; + + /** + * The map of handles to last accesses for input streams. + */ + private Map fInputLastAccessTimes; + + /** + * The map of handles to busy flags. + */ + private Map fInputBusy; + + /** + * The map of handles to open output streams. + */ + private Map fOutputStreams; + + /** + * The map of handles to last accesses for output streams. + */ + private Map fOutputLastAccessTimes; + + /** + * The map of handles to busy flags. + */ + private Map fOutputBusy; + + /** + * The stale handle time. This is the maximum time a handle + * can stay idle in milliseconds. + */ + private long fIdleTimeout; + + /** + * The thread for this Runnable. + */ + private Thread fThread; + + /** + * Flag for whether this Runnable is done. + */ + private boolean fDone; + + /** + * The RepoRemote instance. + */ + private RepoRemote fRepoRemote; + + /** + * The AuthenticationService instance. + */ + private AuthenticationService fAuthService; + + /** + * Default constructor. + */ + public RepoRemoteTransportService() + { + fIdleTimeout = 30000; + fInputStreams = new HashMap(); + fInputLastAccessTimes = new HashMap(); + fInputBusy = new HashMap(); + fOutputStreams = new HashMap(); + fOutputLastAccessTimes = new HashMap(); + fOutputBusy = new HashMap(); + } + + /** + * Set the Idle Timeout value. + * @param timeout The value to set. + */ + public void setIdleTimeout(long timeout) + { + fIdleTimeout = timeout; + } + + /** + * Set the RepoRemote instance. + */ + public void setRepoRemote(RepoRemote remote) + { + fRepoRemote = remote; + } + + /** + * Set the AuthenticationService instance. + */ + public void setAuthenticationService(AuthenticationService service) + { + fAuthService = service; + } + + /** + * The init method. This fires up a thread to check + * for closable streams. + */ + public void init() + { + fThread = new Thread(this); + fDone = false; + fThread.start(); + } + + /** + * The body of this Runnable. + */ + public synchronized void run() + { + while (!fDone) + { + try + { + wait(fIdleTimeout); + } + catch (InterruptedException e) + { + // Do nothing. + } + long now = System.currentTimeMillis(); + List toClose = new ArrayList(); + for (String handle : fInputLastAccessTimes.keySet()) + { + if (fInputBusy.get(handle)) + { + continue; + } + if (now - fInputLastAccessTimes.get(handle) > fIdleTimeout) + { + toClose.add(handle); + } + } + for (String handle : toClose) + { + try + { + fInputStreams.get(handle).close(); + } + catch (IOException e) + { + // Do nothing. + } + fInputStreams.remove(handle); + fInputLastAccessTimes.remove(handle); + fInputBusy.remove(handle); + } + toClose.clear(); + for (String handle : fOutputLastAccessTimes.keySet()) + { + if (fOutputBusy.get(handle)) + { + continue; + } + if (now - fOutputLastAccessTimes.get(handle) > fIdleTimeout) + { + toClose.add(handle); + } + } + for (String handle : toClose) + { + try + { + fOutputStreams.get(handle).close(); + } + catch (IOException e) + { + // Do nothing. + } + fOutputStreams.remove(handle); + fOutputLastAccessTimes.remove(handle); + fOutputBusy.remove(handle); + } + } + } + + /** + * Shutdown the Runnable cleanly. + */ + public void shutDown() + { + synchronized (this) + { + fDone = true; + notifyAll(); + } + try + { + fThread.join(); + } + catch (InterruptedException e) + { + // Do nothing. + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#closeInputHandle(java.lang.String, java.lang.String) + */ + public synchronized void closeInputHandle(String ticket, String handle) + { + fAuthService.validate(ticket); + InputStream in = fInputStreams.get(handle); + if (in != null) + { + try + { + in.close(); + } + catch (IOException e) + { + // Do nothing. + } + fInputStreams.remove(handle); + fInputLastAccessTimes.remove(handle); + fInputBusy.remove(handle); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#closeOutputHandle(java.lang.String, java.lang.String) + */ + public synchronized void closeOutputHandle(String ticket, String handle) + { + fAuthService.validate(ticket); + OutputStream out = fOutputStreams.get(handle); + if (out != null) + { + try + { + out.close(); + } + catch (IOException e) + { + // Do nothing. + } + fOutputStreams.remove(handle); + fOutputLastAccessTimes.remove(handle); + fOutputBusy.remove(handle); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#createDirectory(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef createDirectory(String ticket, NodeRef base, String path) + { + fAuthService.validate(ticket); + return fRepoRemote.createDirectory(base, path); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#createFile(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public String createFile(String ticket, NodeRef base, String path) + { + fAuthService.validate(ticket); + OutputStream out = fRepoRemote.createFile(base, path); + return getOutputHandle(out); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#getListing(java.lang.String, org.alfresco.service.cmr.repository.NodeRef) + */ + public Map getListing(String ticket, NodeRef dir) + { + fAuthService.validate(ticket); + return fRepoRemote.getListing(dir); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#getRoot(java.lang.String) + */ + public NodeRef getRoot(String ticket) + { + fAuthService.validate(ticket); + return fRepoRemote.getRoot(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#lookup(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public NodeRef lookup(String ticket, NodeRef base, String path) + { + fAuthService.validate(ticket); + return fRepoRemote.lookup(base, path); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#readFile(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public String readFile(String ticket, NodeRef base, String path) + { + fAuthService.validate(ticket); + InputStream in = fRepoRemote.readFile(base, path); + return getInputHandle(in); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#readFile(java.lang.String, org.alfresco.service.cmr.repository.NodeRef) + */ + public String readFile(String ticket, NodeRef fileRef) + { + fAuthService.validate(ticket); + InputStream in = fRepoRemote.readFile(fileRef); + return getInputHandle(in); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#readInput(java.lang.String, java.lang.String, int) + */ + public byte[] readInput(String ticket, String handle, int count) + { + fAuthService.validate(ticket); + InputStream in = null; + synchronized (this) + { + in = fInputStreams.get(handle); + if (in == null) + { + throw new AlfrescoRuntimeException("Invalid Input Handle."); + } + fInputBusy.put(handle, true); + fInputLastAccessTimes.put(handle, System.currentTimeMillis()); + } + byte [] buff = new byte[count]; + try + { + int read = in.read(buff); + if (read == -1) + { + read = 0; + } + if (read != count) + { + byte [] newBuff = new byte[read]; + for (int i = 0; i < read; i++) + { + newBuff[i] = buff[i]; + } + return newBuff; + } + return buff; + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("I/O Error."); + } + finally + { + synchronized (this) + { + fInputBusy.put(handle, false); + } + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#removeNode(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public void removeNode(String ticket, NodeRef base, String path) + { + fAuthService.validate(ticket); + fRepoRemote.removeNode(base, path); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#removeNode(java.lang.String, org.alfresco.service.cmr.repository.NodeRef) + */ + public void removeNode(String ticket, NodeRef toRemove) + { + fAuthService.validate(ticket); + fRepoRemote.removeNode(toRemove); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#rename(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) + */ + public void rename(String ticket, NodeRef base, String src, String dst) + { + fAuthService.validate(ticket); + fRepoRemote.rename(base, src, dst); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#writeFile(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public String writeFile(String ticket, NodeRef base, String path) + { + fAuthService.validate(ticket); + OutputStream out = fRepoRemote.writeFile(base, path); + return getOutputHandle(out); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.remote.RepoRemoteTransport#writeOutput(java.lang.String, java.lang.String, byte[], int) + */ + public void writeOutput(String ticket, String handle, byte[] buff, int count) + { + fAuthService.validate(ticket); + OutputStream out = null; + synchronized (this) + { + out = fOutputStreams.get(handle); + if (out == null) + { + throw new AlfrescoRuntimeException("Invalid Output Handle."); + } + fOutputBusy.put(handle, true); + fOutputLastAccessTimes.put(handle, System.currentTimeMillis()); + } + try + { + out.write(buff, 0, count); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("I/O Errror."); + } + finally + { + synchronized (this) + { + fOutputBusy.put(handle, false); + } + } + } + + private synchronized String getOutputHandle(OutputStream out) + { + String handle = GUID.generate(); + fOutputStreams.put(handle, out); + fOutputLastAccessTimes.put(handle, System.currentTimeMillis()); + fOutputBusy.put(handle, false); + return handle; + } + + private synchronized String getInputHandle(InputStream in) + { + String handle = GUID.generate(); + fInputStreams.put(handle, in); + fInputLastAccessTimes.put(handle, System.currentTimeMillis()); + fInputBusy.put(handle, false); + return handle; + } +} diff --git a/source/java/org/alfresco/service/cmr/remote/RepoRemote.java b/source/java/org/alfresco/service/cmr/remote/RepoRemote.java new file mode 100644 index 0000000000..7dbc0a429a --- /dev/null +++ b/source/java/org/alfresco/service/cmr/remote/RepoRemote.java @@ -0,0 +1,98 @@ +/** + * + */ +package org.alfresco.service.cmr.remote; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A highly simplified remote interface for the repo. + * @author britt + */ +public interface RepoRemote +{ + /** + * Get the root node of the SpacesStore repo. + * @return The root node ref. + */ + public NodeRef getRoot(); + + /** + * Get a listing of a directory. + * @param dir The node ref of the directory. + * @return A Map of names to node refs. + */ + public Map getListing(NodeRef dir); + + /** + * Lookup a node by path relative to a node. + * @param base The base node ref. + * @param path The relative path. + * @return The node ref or null. + */ + public NodeRef lookup(NodeRef base, String path); + + /** + * Create a file relative to a base node. + * @param base The base node ref. + * @param path The relative path. + * @return An OutputStream. + */ + public OutputStream createFile(NodeRef base, String path); + + /** + * Write to an already existing file. + * @param base The base node ref. + * @param path The relative path. + * @return An OutputStream + */ + public OutputStream writeFile(NodeRef base, String path); + + /** + * Create a new directory. + * @param base The base node ref. + * @param path The relative path. + * @return The node ref to the newly created directory. + */ + public NodeRef createDirectory(NodeRef base, String path); + + /** + * Remove a node directly. + * @param toRemove The node ref to remove. + */ + public void removeNode(NodeRef toRemove); + + /** + * Remove a node via a relative path. + * @param base The base node ref. + * @param path The relative path. + */ + public void removeNode(NodeRef base, String path); + + /** + * Rename a node + * @param base The base node ref. + * @param src The relative source path. + * @param dst The relative target path. + */ + public void rename(NodeRef base, String src, String dst); + + /** + * Read a file directly. + * @param fileRef The node ref of the file. + * @return An InputStream. + */ + public InputStream readFile(NodeRef fileRef); + + /** + * Read a file from a relative path. + * @param base The base node ref. + * @param path The relative path to the file. + * @return An InputStream. + */ + public InputStream readFile(NodeRef base, String path); +} diff --git a/source/java/org/alfresco/service/cmr/remote/RepoRemoteTransport.java b/source/java/org/alfresco/service/cmr/remote/RepoRemoteTransport.java new file mode 100644 index 0000000000..a18872a349 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/remote/RepoRemoteTransport.java @@ -0,0 +1,131 @@ +/** + * + */ +package org.alfresco.service.cmr.remote; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Over the wire, and authentication safe flavor of + * RepoRemote interface. + * @author britt + */ +public interface RepoRemoteTransport +{ + /** + * Get the root node of the SpacesStore repo. + * @return The root node ref. + */ + public NodeRef getRoot(String ticket); + + /** + * Get a listing of a directory. + * @param dir The node ref of the directory. + * @return A Map of names to node refs. + */ + public Map getListing(String ticket, NodeRef dir); + + /** + * Lookup a node by path relative to a node. + * @param base The base node ref. + * @param path The relative path. + * @return The node ref or null. + */ + public NodeRef lookup(String ticket, NodeRef base, String path); + + /** + * Create a file relative to a base node. + * @param base The base node ref. + * @param path The relative path. + * @return A handle. + */ + public String createFile(String ticket, NodeRef base, String path); + + /** + * Write to an already existing file. + * @param base The base node ref. + * @param path The relative path. + * @return A handle. + */ + public String writeFile(String ticket, NodeRef base, String path); + + /** + * Create a new directory. + * @param base The base node ref. + * @param path The relative path. + * @return The node ref to the newly created directory. + */ + public NodeRef createDirectory(String ticket, NodeRef base, String path); + + /** + * Remove a node directly. + * @param toRemove The node ref to remove. + */ + public void removeNode(String ticket, NodeRef toRemove); + + /** + * Remove a node via a relative path. + * @param base The base node ref. + * @param path The relative path. + */ + public void removeNode(String ticket, NodeRef base, String path); + + /** + * Rename a node + * @param base The base node ref. + * @param src The relative source path. + * @param dst The relative target path. + */ + public void rename(String ticket, NodeRef base, String src, String dst); + + /** + * Read a file directly. + * @param fileRef The node ref of the file. + * @return A handle. + */ + public String readFile(String ticket, NodeRef fileRef); + + /** + * Read a file from a relative path. + * @param base The base node ref. + * @param path The relative path to the file. + * @return A handle. + */ + public String readFile(String ticket, NodeRef base, String path); + + /** + * Read a block of bytes over the wire. + * @param ticket The authentication ticket. + * @param handle The remote handle. + * @param count The number of bytes to try to read. + * @return A buffer of the bytes read. Length is 0 at EOF. + */ + public byte[] readInput(String ticket, String handle, int count); + + /** + * Write a portion of a block of bytes over the wire. + * @param ticket The authentication ticket. + * @param handle The remote handle. + * @param buff The buffer with data. + * @param count The number of bytes to write. + */ + public void writeOutput(String ticket, String handle, byte[] buff, int count); + + /** + * Close a remote InputStream. + * @param ticket The authentication ticket. + * @param handle The handle. + */ + public void closeInputHandle(String ticket, String handle); + + /** + * Close a remote OutputStream. + * @param ticket The authentication ticket. + * @param handle The handle. + */ + public void closeOutputHandle(String ticket, String handle); +}