Swift – SE.S21 Share - DM Remote Store - WIP

- continuing to fill out implementation
 - fixing minor issues come up during testing against Share

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28373 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2011-06-13 20:21:28 +00:00
parent 227b9bf7bf
commit 313afd42da
2 changed files with 79 additions and 114 deletions

View File

@@ -42,7 +42,6 @@ import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.security.permissions.noop.PermissionServiceNOOPImpl;
import org.alfresco.service.cmr.model.FileExistsException; import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderUtil; import org.alfresco.service.cmr.model.FileFolderUtil;
@@ -55,7 +54,6 @@ import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
@@ -75,8 +73,6 @@ import org.springframework.extensions.webscripts.WebScriptResponse;
* in the Sites folder. Dashboard pages and component bindings are remapped to take advantage * in the Sites folder. Dashboard pages and component bindings are remapped to take advantage
* of inherited permissions in the appropriate root site folder, ensuring that only valid * of inherited permissions in the appropriate root site folder, ensuring that only valid
* users can write to the appropriate configuration objects. * users can write to the appropriate configuration objects.
* <p>
* System folders are used to hide configuration from general folder browsing UI.
* *
* @see BaseRemoteStore for the available API methods. * @see BaseRemoteStore for the available API methods.
* *
@@ -88,6 +84,7 @@ public class ADMRemoteStore extends BaseRemoteStore
private static final String SURF_CONFIG = "surf-config"; private static final String SURF_CONFIG = "surf-config";
// patterns used to match site and user specific configuration locations
private static final Pattern USER_PATTERN_1 = Pattern.compile(".*/components/.*\\.user~(.*)~.*"); private static final Pattern USER_PATTERN_1 = Pattern.compile(".*/components/.*\\.user~(.*)~.*");
private static final Pattern USER_PATTERN_2 = Pattern.compile(".*/pages/user/(.*?)(/.*)?$"); private static final Pattern USER_PATTERN_2 = Pattern.compile(".*/pages/user/(.*?)(/.*)?$");
private static final Pattern SITE_PATTERN_1 = Pattern.compile(".*/components/.*\\.site~(.*)~.*"); private static final Pattern SITE_PATTERN_1 = Pattern.compile(".*/components/.*\\.site~(.*)~.*");
@@ -161,10 +158,10 @@ public class ADMRemoteStore extends BaseRemoteStore
protected void lastModified(final WebScriptResponse res, final String store, final String path) protected void lastModified(final WebScriptResponse res, final String store, final String path)
throws IOException throws IOException
{ {
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
final FileInfo fileInfo = resolveFilePath(path); final FileInfo fileInfo = resolveFilePath(path);
if (fileInfo == null) if (fileInfo == null)
@@ -195,10 +192,10 @@ public class ADMRemoteStore extends BaseRemoteStore
{ {
// TODO: could only allow appropriate users to read config docs i.e. site specific... // TODO: could only allow appropriate users to read config docs i.e. site specific...
// but currently GET requests need to run unauthenticated before user login occurs. // but currently GET requests need to run unauthenticated before user login occurs.
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
final FileInfo fileInfo = resolveFilePath(path); final FileInfo fileInfo = resolveFilePath(path);
if (fileInfo == null || fileInfo.isFolder()) if (fileInfo == null || fileInfo.isFolder())
@@ -295,10 +292,10 @@ public class ADMRemoteStore extends BaseRemoteStore
@Override @Override
protected void hasDocument(final WebScriptResponse res, final String store, final String path) throws IOException protected void hasDocument(final WebScriptResponse res, final String store, final String path) throws IOException
{ {
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
final FileInfo fileInfo = resolveFilePath(path); final FileInfo fileInfo = resolveFilePath(path);
@@ -376,6 +373,7 @@ public class ADMRemoteStore extends BaseRemoteStore
if (fileInfo == null || fileInfo.isFolder()) if (fileInfo == null || fileInfo.isFolder())
{ {
res.setStatus(Status.STATUS_NOT_FOUND); res.setStatus(Status.STATUS_NOT_FOUND);
return;
} }
ContentWriter writer = contentService.getWriter(fileInfo.getNodeRef(), ContentModel.PROP_CONTENT, true); ContentWriter writer = contentService.getWriter(fileInfo.getNodeRef(), ContentModel.PROP_CONTENT, true);
@@ -400,6 +398,7 @@ public class ADMRemoteStore extends BaseRemoteStore
if (fileInfo == null || fileInfo.isFolder()) if (fileInfo == null || fileInfo.isFolder())
{ {
res.setStatus(Status.STATUS_NOT_FOUND); res.setStatus(Status.STATUS_NOT_FOUND);
return;
} }
this.nodeService.deleteNode(fileInfo.getNodeRef()); this.nodeService.deleteNode(fileInfo.getNodeRef());
@@ -423,10 +422,10 @@ public class ADMRemoteStore extends BaseRemoteStore
protected void listDocuments(final WebScriptResponse res, final String store, final String path, final boolean recurse) protected void listDocuments(final WebScriptResponse res, final String store, final String path, final boolean recurse)
throws IOException throws IOException
{ {
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
res.setContentType("text/plain;charset=UTF-8"); res.setContentType("text/plain;charset=UTF-8");
@@ -439,36 +438,7 @@ public class ADMRemoteStore extends BaseRemoteStore
try try
{ {
final boolean debug = logger.isDebugEnabled(); outputFileNodes(res.getWriter(), fileInfo, aquireSurfConfigRef(path, false), "*", recurse);
final Writer out = res.getWriter();
final NodeRef surfConfigRef = aquireSurfConfigRef(path, false);
Map<NodeRef, String> nameCache = new HashMap<NodeRef, String>();
List<FileInfo> files = fileFolderService.search(fileInfo.getNodeRef(), "*", true, false, recurse);
for (final FileInfo file : files)
{
// walking up the parent tree manually until the "surf-config" parent is hit
// and manually appending the rest of the cm:name path down to the node.
StringBuilder displayPath = new StringBuilder(64);
NodeRef ref = unprotNodeService.getPrimaryParent(file.getNodeRef()).getParentRef();
while (!ref.equals(surfConfigRef))
{
String name = nameCache.get(ref);
if (name == null)
{
name = (String)unprotNodeService.getProperty(ref, ContentModel.PROP_NAME);
nameCache.put(ref, name);
}
displayPath.insert(0, '/');
displayPath.insert(0, name);
ref = unprotNodeService.getPrimaryParent(ref).getParentRef();
}
out.write("/alfresco/site-data/");
out.write(displayPath.toString());
out.write(file.getName());
out.write('\n');
if (debug) logger.debug(" /alfresco/site-data/" + displayPath.toString() + "/" + file.getName());
}
} }
catch (AccessDeniedException ae) catch (AccessDeniedException ae)
{ {
@@ -499,10 +469,10 @@ public class ADMRemoteStore extends BaseRemoteStore
protected void listDocuments(final WebScriptResponse res, final String store, final String path, final String pattern) protected void listDocuments(final WebScriptResponse res, final String store, final String path, final String pattern)
throws IOException throws IOException
{ {
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
res.setContentType("text/plain;charset=UTF-8"); res.setContentType("text/plain;charset=UTF-8");
@@ -519,48 +489,21 @@ public class ADMRemoteStore extends BaseRemoteStore
filePattern = "*"; filePattern = "*";
} }
final boolean debug = logger.isDebugEnabled(); if (logger.isDebugEnabled())
logger.debug("listDocuments() pattern: " + pattern);
//
// TODO: remove use of SearchService here! Jan wrote a DB version for AVM - same for DM required.
//
// encode the qname match - and convert back any wildcard characters
/*String qname = ISO9075.encode(filePattern);
qname = qname.replace(ISO9075_ASTERIX, "*");
final String encPath = toQNamePath(path);
final StringBuilder query = new StringBuilder(128);
query.append("+PATH:\"/").append(NamespaceService.APP_MODEL_PREFIX).append(":").append(ADMROOT)
.append(encPath != null && encPath.length() != 0 ? encPath : "")
.append("//*\" +QNAME:")
.append(qname);
final Writer out = res.getWriter();
final int cropPoint = rootPath.length() + 1;
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, store);
String sQuery = query.toString();
ResultSet resultSet = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, sQuery);
try try
{ {
List<NodeRef> nodes = resultSet.getNodeRefs(); outputFileNodes(res.getWriter(), fileInfo, aquireSurfConfigRef(path, false), pattern, false);
for (final NodeRef nodeRef : nodes) }
{ catch (AccessDeniedException ae)
String name = (String)unprotNodeService.getProperty(nodeRef, ContentModel.PROP_NAME); {
Path path = unprotNodeService.getPath(nodeRef); res.setStatus(Status.STATUS_UNAUTHORIZED);
// TODO: optimize this section
String displayPath = path.toDisplayPath(unprotNodeService, new PermissionServiceNOOPImpl());
out.write(displayPath.substring(cropPoint));
out.write('/');
out.write(name);
out.write('\n');
}
} }
finally finally
{ {
resultSet.close(); res.getWriter().close();
}*/ }
// TODO: close writer!
return null; return null;
} }
}, AuthenticationUtil.getSystemUserName()); }, AuthenticationUtil.getSystemUserName());
@@ -600,7 +543,7 @@ public class ADMRemoteStore extends BaseRemoteStore
// break down the path into its component elements // break down the path into its component elements
List<String> pathElements = new ArrayList<String>(4); List<String> pathElements = new ArrayList<String>(4);
final StringTokenizer t = new StringTokenizer(encodePath(path), "/"); final StringTokenizer t = new StringTokenizer(encodePath(path), "/");
// we require paths of the form /alfresco/site-data/<objecttype>[/<folder>]/<file>.xml // the store requires paths of the form /alfresco/site-data/<objecttype>[/<folder>]/<file>.xml
if (t.countTokens() >= 3) if (t.countTokens() >= 3)
{ {
t.nextToken(); // skip /alfresco t.nextToken(); // skip /alfresco
@@ -676,7 +619,7 @@ public class ADMRemoteStore extends BaseRemoteStore
siteName = matcher.group(1); siteName = matcher.group(1);
} }
NodeRef surfConfigRef; NodeRef surfConfigRef = null;
if (userId != null) if (userId != null)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
@@ -687,7 +630,11 @@ public class ADMRemoteStore extends BaseRemoteStore
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("...resolved site path id: " + siteName); logger.debug("...resolved site path id: " + siteName);
surfConfigRef = getSurfConfigNodeRef(getSiteNodeRef(siteName), create); NodeRef siteRef = getSiteNodeRef(siteName);
if (siteRef != null)
{
surfConfigRef = getSurfConfigNodeRef(siteRef, create);
}
} }
else else
{ {
@@ -726,19 +673,21 @@ public class ADMRemoteStore extends BaseRemoteStore
NodeRef surfConfigRef = this.unprotNodeService.getChildByName( NodeRef surfConfigRef = this.unprotNodeService.getChildByName(
rootRef, ContentModel.ASSOC_CONTAINS, SURF_CONFIG); rootRef, ContentModel.ASSOC_CONTAINS, SURF_CONFIG);
// //
// TODO: protect with RRW - keyed from rootRef? // TODO: Does this need protecting with RRW - keyed from rootRef?
// As given the use cases, only the surf-config folder in Sites has the potential
// for multi-user WRITE access e.g. 2 users creating their dashboards at same time
// - which could be solved by creating the surf-config folder as part of the patch code
// and on bootstrap for new repo also.
// As the site level surf-config folders will be created by which ever user creates the
// site - no READ access will be possible until then anyway...
// //
if (surfConfigRef == null && create) if (create && surfConfigRef == null)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("'surf-config' system folder not found under path, creating..."); logger.debug("'surf-config' folder not found under path, creating...");
QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, SURF_CONFIG); QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, SURF_CONFIG);
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(1, 1.0f); Map<QName, Serializable> properties = new HashMap<QName, Serializable>(1, 1.0f);
properties.put(ContentModel.PROP_NAME, (Serializable) SURF_CONFIG); properties.put(ContentModel.PROP_NAME, (Serializable) SURF_CONFIG);
//
// TODO: change this to TYPE_SYSTEMFOLDER before commit? that won't stop config files getting found...
// suggest new model type that is not cm:content - and is NOT fts enabled...
//
ChildAssociationRef ref = this.unprotNodeService.createNode( ChildAssociationRef ref = this.unprotNodeService.createNode(
rootRef, ContentModel.ASSOC_CONTAINS, assocQName, ContentModel.TYPE_FOLDER, properties); rootRef, ContentModel.ASSOC_CONTAINS, assocQName, ContentModel.TYPE_FOLDER, properties);
surfConfigRef = ref.getChildRef(); surfConfigRef = ref.getChildRef();
@@ -766,30 +715,46 @@ public class ADMRemoteStore extends BaseRemoteStore
} }
/** /**
* Traverse a Node and recursively output the file paths it contains. * Output the matching file paths a node contains based on a pattern search.
* *
* @param out Writer for output - relative paths separated by newline characters * @param out Writer for output - relative paths separated by newline characters
* @param node The FileInfo node to traverse * @param surfConfigRef Surf-Config folder
* @param fileInfo The FileInfo node to use as the parent
* @param pattern Optional pattern to match filenames against * @param pattern Optional pattern to match filenames against
* @param recurse True to recurse sub-directories * @param recurse True to recurse sub-directories
* *
* @throws IOException * @throws IOException
*/ */
private void traverseNode(Writer out, FileInfo node, String pattern, boolean recurse) private void outputFileNodes(Writer out, FileInfo fileInfo, NodeRef surfConfigRef, String pattern, boolean recurse)
throws IOException throws IOException
{ {
final int cropPoint = 0;//TODO:rootPath.length() + 1; final boolean debug = logger.isDebugEnabled();
final Map<NodeRef, String> nameCache = new HashMap<NodeRef, String>();
// TODO: this could be optimized a lot: final List<FileInfo> files = fileFolderService.search(fileInfo.getNodeRef(), pattern, true, false, recurse);
// 1. Perform the traverse and search by hand (is this faster?) using small nodeService
// 2. Keep track of parent path cm:name values so Path is not required
List<FileInfo> files = this.fileFolderService.search(getRootNodeRef(), pattern, true, false, recurse);
for (final FileInfo file : files) for (final FileInfo file : files)
{ {
Path path = this.unprotNodeService.getPath(file.getNodeRef()); // walking up the parent tree manually until the "surf-config" parent is hit
String displayPath = path.toDisplayPath(this.unprotNodeService, new PermissionServiceNOOPImpl()); // and manually appending the rest of the cm:name path down to the node.
out.write(displayPath.substring(cropPoint)); StringBuilder displayPath = new StringBuilder(64);
out.write("\n"); NodeRef ref = unprotNodeService.getPrimaryParent(file.getNodeRef()).getParentRef();
while (!ref.equals(surfConfigRef))
{
String name = nameCache.get(ref);
if (name == null)
{
name = (String)unprotNodeService.getProperty(ref, ContentModel.PROP_NAME);
nameCache.put(ref, name);
}
displayPath.insert(0, '/');
displayPath.insert(0, name);
ref = unprotNodeService.getPrimaryParent(ref).getParentRef();
}
out.write("/alfresco/site-data/");
out.write(displayPath.toString());
out.write(file.getName());
out.write('\n');
if (debug) logger.debug(" /alfresco/site-data/" + displayPath.toString() + file.getName());
} }
} }
} }

View File

@@ -159,10 +159,10 @@ public class AVMRemoteStore extends BaseRemoteStore
return; return;
} }
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
ContentReader reader; ContentReader reader;
try try
@@ -251,10 +251,10 @@ public class AVMRemoteStore extends BaseRemoteStore
@Override @Override
protected void createDocument(final WebScriptResponse res, final String store, final String path, final InputStream content) protected void createDocument(final WebScriptResponse res, final String store, final String path, final InputStream content)
{ {
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
String avmPath = buildAVMPath(store, path); String avmPath = buildAVMPath(store, path);
try try
@@ -298,10 +298,10 @@ public class AVMRemoteStore extends BaseRemoteStore
@Override @Override
protected void createDocuments(final WebScriptResponse res, final String store, final InputStream in) protected void createDocuments(final WebScriptResponse res, final String store, final InputStream in)
{ {
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
try try
{ {
@@ -384,10 +384,10 @@ public class AVMRemoteStore extends BaseRemoteStore
return; return;
} }
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
try try
{ {
@@ -420,10 +420,10 @@ public class AVMRemoteStore extends BaseRemoteStore
return; return;
} }
AuthenticationUtil.runAs(new RunAsWork<Object>() AuthenticationUtil.runAs(new RunAsWork<Void>()
{ {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
public Object doWork() throws Exception public Void doWork() throws Exception
{ {
try try
{ {