Merge from WCM-CUOMO2 to HEAD - Remote API

- Revisions 11077, 11081, 11107, 11155, 11452, 11537, 11568, 11571, 11767, 11804, 11808
- Adjustments to the AVM Remote Store to fully allow it to interact with any AVM store based on store id and web application id.  The latter is provided to allow WCM Web Project stores to play nicely with non-WCM stores (like sitestore).
- Additional arguments to AVM Remote Store can be passed in as request parameters or as tokenized strings (/s/<store>/w/<webapp>) which allows for URL addressability using a remotestore prefix.
- Additional remote API's for web framework to allow for AVM metadata query and some redirection
- Best effort to merge stuff without breaking any of Mark or Jan's code.  Will work at manual integration of REST API bits tomorrow.  Expect good things.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@11811 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Michael Uzqu
2008-11-10 08:06:36 +00:00
parent 0d14bd4dd6
commit ce8299f126
30 changed files with 676 additions and 115 deletions

View File

@@ -3,7 +3,11 @@
<description>Remote service mirroring the Store interface - to an AVM store</description>
<url>/remotestore/{method}</url>
<url>/remotestore/{method}/{path}</url>
<authentication>none</authentication>
<url>/remotestore/{method}/s/{store}</url>
<url>/remotestore/{method}/s/{store}/{path}</url>
<url>/remotestore/{method}/s/{store}/w/{webapp}</url>
<url>/remotestore/{method}/s/{store}/w/{webapp}/{path}</url>
<authentication>guest</authentication>
<transaction>required</transaction>
<format default="">argument</format>
</webscript>

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Web Content Management - List Sandboxes</shortname>
<description>Web Content Management - List Sandboxes</description>
<url>/api/wcm/sandbox/list</url>
<format default="html">argument</format>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,16 @@
{
"results" : [
<#assign first = true>
<#list results as result>
<#if first == false>,</#if>
{
"name" : "${result.name}"
}
<#assign first = false>
</#list>
]
}

View File

@@ -0,0 +1,29 @@
var webproject = args["webproject"];
var dmType = "{http://www.alfresco.org/model/wcmappmodel/1.0}webfolder";
var query = "TYPE:\"" + dmType + "\"";
var webprojects = search.luceneSearch(query);
var results = null;
var avmStoreId = null;
// walk through the projects, get the avm store id (for staging)
for(var i = 0; i < webprojects.length; i++)
{
var projName = webprojects[i].name;
if(projName == webproject)
{
avmStoreId = webprojects[i].properties["{http://www.alfresco.org/model/wcmappmodel/1.0}avmstore"];
}
}
if(avmStoreId != null)
{
results = new Array();
results[results.length] = avm.lookupStore(avmStoreId);
results[results.length] = avm.lookupStore(avmStoreId + "--admin");
}
model.results = results;

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Web Content Management - Sandbox GET</shortname>
<description>Web Content Management - Sandbox GET</description>
<url>/api/wcm/sandbox/{id}</url>
<format default="json"/>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,19 @@
function main()
{
// Get the node from the URL
var pathSegments = url.match.split("/");
var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/"));
var node = search.findNode(pathSegments[2], reference);
// 404 if the node is not found
if (node == null)
{
status.setCode(status.STATUS_NOT_FOUND, "The node could not be found");
return;
}
// Get the tags of the node
model.tags = node.tags;
}
main();

View File

@@ -0,0 +1,5 @@
[
<#list tags as tag>
${jsonUtils.encodeJSONString(tag)}<#if tag_has_next>,</#if>
</#list>
]

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Web Content Management - Sandbox POST</shortname>
<description>Web Content Management - Sandbox POST</description>
<url>/api/wcm/sandbox</url>
<format default="json"/>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,19 @@
function main()
{
// Get the node from the URL
var pathSegments = url.match.split("/");
var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/"));
var node = search.findNode(pathSegments[2], reference);
// 404 if the node is not found
if (node == null)
{
status.setCode(status.STATUS_NOT_FOUND, "The node could not be found");
return;
}
// Get the tags of the node
model.tags = node.tags;
}
main();

View File

@@ -0,0 +1,5 @@
[
<#list tags as tag>
${jsonUtils.encodeJSONString(tag)}<#if tag_has_next>,</#if>
</#list>
]

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Web Content Management - List Web Projects</shortname>
<description>Web Content Management - List Web Projects</description>
<url>/api/wcm/webproject/list</url>
<format default="html">argument</format>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,18 @@
{
"results" : [
<#assign first = true>
<#list results as result>
<#if first == false>,</#if>
{
"name" : "${result.name}"
,
"nodeRef" : "${result.nodeRef}"
}
<#assign first = false>
</#list>
]
}

View File

@@ -0,0 +1,5 @@
var dmType = "{http://www.alfresco.org/model/wcmappmodel/1.0}webfolder";
var query = "TYPE:\"" + dmType + "\"";
var results = search.luceneSearch(query);
model.results = results;

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Web Content Management - Web Project GET</shortname>
<description>Web Content Management - Web Project GET</description>
<url>/api/wcm/webproject</url>
<format default="html"/>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,11 @@
<#if result?exists>
{
"name" : "${result.name}"
,
"id" : "${result.webProjectId}"
,
"stagingSandboxId" : "${result.sandboxId}"
,
"stagingStoreId" : "${result.storeId}"
}
</#if>

View File

@@ -0,0 +1,23 @@
var id = args["id"];
var dmType = "{http://www.alfresco.org/model/wcmappmodel/1.0}webfolder";
var query = "TYPE:\"" + dmType + "\"";
var webprojects = search.luceneSearch(query);
var result = { };
// walk through the projects, get the avm store id (for staging)
for(var i = 0; i < webprojects.length; i++)
{
var projName = webprojects[i].name;
if(projName == id)
{
result["name"] = projName;
result["webProjectId"] = id;
result["storeId"] = webprojects[i].properties["{http://www.alfresco.org/model/wcmappmodel/1.0}avmstore"];
result["sandboxId"] = webprojects[i].properties["{http://www.alfresco.org/model/wcmappmodel/1.0}avmstore"];
}
}
model.result = result;

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Web Content Management - Web Project POST</shortname>
<description>Web Content Management - Web Project POST</description>
<url>/api/wcm/webproject</url>
<format default="json"/>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,30 @@
// arguments
var jsonString = args["json"];
model.status = null;
if(jsonString != null)
{
// load arguments into object
var json = eval('(' + jsonString + ')');
// attributes
var id = json.id;
var title = json.title;
var description = json.description;
// TODO: create the web project
// set back onto return
model.webProjectId = id;
model.storeId = id;
model.sandboxId = id;
model.status = 'ok';
}
if(model.status == null)
{
model.status = 'error';
}

View File

@@ -0,0 +1,13 @@
{
"status" : "${status}"
<#if status == 'ok'>
,
"webProjectId" : "${webProjectId}"
,
"sandboxId" : "${sandboxId}"
,
"storeId" : "${storeId}"
</#if>
}

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>AVM Metadata Retrieval Service</shortname>
<description>AVM Metadata Retrieval Service</description>
<url>/webframework/avm/metadata/{storeId}/{webappId}/{path}</url>
<format default="html">argument</format>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,87 @@
{
<@serialize object=object includeChildren=includeChildren includeContent=includeContent/>
}
<#macro serialize object includeChildren includeContent>
"isContainer" : ${object.isContainer?string}
,
"isDocument" : ${object.isDocument?string}
,
"url" : "${object.url}"
,
"downloadUrl" : "${object.downloadUrl}"
<#if object.mimetype?exists>
,
"mimetype" : "${object.mimetype}"
</#if>
,
"size" : "${object.size}"
,
"displayPath" : "${object.displayPath}"
,
"qnamePath" : "${object.qnamePath}"
,
"icon16" : "${object.icon16}"
,
"icon32" : "${object.icon32}"
,
"isLocked" : ${object.isLocked?string}
,
"id" : "${object.id}"
,
"nodeRef" : "${object.nodeRef}"
,
"name" : "${object.name}"
,
"type" : "${object.type}"
,
"isCategory" : ${object.isCategory?string}
<#if object.properties?exists>
,
"properties" :
{
<#assign first = true>
<#list object.properties?keys as key>
<#if object.properties[key]?exists>
<#assign val = object.properties[key]>
<#if val?is_string == true>
<#if first == false>,</#if>
"${key}" : "${val?js_string}"
<#assign first = false>
<#elseif val?is_date == true>
<#if first == false>,</#if>
"${key}" : "${val?datetime}"
<#assign first = false>
<#elseif val?is_boolean == true>
<#if first == false>,</#if>
"${key}" : "${val?string}"
<#assign first = false>
</#if>
</#if>
</#list>
}
</#if>
<#if includeChildren && object.children?exists>
,
"children" :
[
<#assign first = true>
<#list object.children as child>
<#if first == false>
,
</#if>
{
<@serialize object=child includeChildren=false includeContent=includeContent/>
}
<#assign first = false>
</#list>
]
<#else>
,
"children" : []
</#if>
</#macro>

View File

@@ -0,0 +1,17 @@
model.includeChildren = true;
model.includeContent = false;
var object = null;
var storeId = url.templateArgs["storeId"];
var webappId = url.templateArgs["webappId"];
var path = url.templateArgs["path"];
var storeRootNode = avm.lookupStoreRoot(storeId);
if (storeRootNode != null)
{
var path = storeRootNode.path + "/" + webappId + "/" + path;
object = avm.lookupNode(path);
}
model.object = object;

View File

@@ -0,0 +1,9 @@
<webscript>
<shortname>JSF Redirection Handler</shortname>
<description>JSF Redirection Handler</description>
<url>/webframework/redirect/jsf-client/{command}/{objectType}/{storeType}/{storeId}/{nodeId}</url>
<url>/webframework/redirect/jsf-client/{command}/{objectType}/{webProjectId}</url>
<format default="html">argument</format>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,5 @@
<#if redirectUrl?exists>
<script language="Javascript">
window.location.href = "${redirectUrl}";
</script>
</#if>

View File

@@ -0,0 +1,40 @@
model.command = url.templateArgs["command"];
model.objectType = url.templateArgs["objectType"];
if("browse" == model.command)
{
if("node" == model.objectType)
{
var storeType = url.templateArgs["storeType"];
var storeId = url.templateArgs["storeId"];
var nodeId = url.templateArgs["nodeId"];
model.redirectUrl = "/alfresco/n/browse/"+storeType+"/"+storeId+"/"+nodeId;
}
if("webproject" == model.objectType)
{
model.webProjectId = url.templateArgs["webProjectId"];
// look up the web project
var dmType = "{http://www.alfresco.org/model/wcmappmodel/1.0}webfolder";
var query = "TYPE:\"" + dmType + "\"";
var webprojects = search.luceneSearch(query);
// walk through the projects, get the avm store id (for staging)
for(var i = 0; i < webprojects.length; i++)
{
var projId = webprojects[i].name;
if(projId == model.webProjectId)
{
var storeType = webprojects[i].properties["{http://www.alfresco.org/model/system/1.0}store-protocol"];
var storeId = webprojects[i].properties["{http://www.alfresco.org/model/system/1.0}store-identifier"];
var nodeId = webprojects[i].properties["{http://www.alfresco.org/model/system/1.0}node-uuid"];
model.redirectUrl = "/alfresco/n/browse/"+storeType+"/"+storeId+"/"+nodeId;
}
}
}
}

View File

@@ -10,8 +10,11 @@
"url" : "${object.url}"
,
"downloadUrl" : "${object.downloadUrl}"
<#if object.mimetype?exists>
,
"mimetype" : "${mimetype}"
"mimetype" : "${object.mimetype}"
</#if>
,
"size" : "${object.size}"
,
@@ -57,7 +60,7 @@
<#assign first = false>
<#elseif val?is_boolean == true>
<#if first == false>,</#if>
"${key}" : "${val}"
"${key}" : "${val?string}"
<#assign first = false>
</#if>
</#if>

View File

@@ -32,7 +32,7 @@ else
}
else
{
path = "/Company Home" + path;
//path = "/Company Home" + path;
}
// look up the content by path
@@ -40,4 +40,3 @@ else
}
model.object = object;
model.mimetype = object.mimetype;

View File

@@ -246,22 +246,28 @@
<property name="mimetypeService" ref="MimetypeService" />
<property name="avmService" ref="AVMService" />
<property name="searchService" ref="SearchService" />
<!--
<property name="rootPath"><value>alfresco</value></property>
<property name="store"><value>sitestore</value></property>
-->
</bean>
<bean id="webscript.org.alfresco.repository.store.remoteavm.post" class="org.alfresco.repo.web.scripts.bean.AVMRemoteStore" parent="webscript">
<property name="mimetypeService" ref="MimetypeService" />
<property name="avmService" ref="AVMService" />
<property name="searchService" ref="SearchService" />
<!--
<property name="rootPath"><value>alfresco</value></property>
<property name="store"><value>sitestore</value></property>
-->
</bean>
<bean id="webscript.org.alfresco.repository.store.remoteavm.delete" class="org.alfresco.repo.web.scripts.bean.AVMRemoteStore" parent="webscript">
<property name="mimetypeService" ref="MimetypeService" />
<property name="avmService" ref="AVMService" />
<property name="searchService" ref="SearchService" />
<!--
<property name="rootPath"><value>alfresco</value></property>
<property name="store"><value>sitestore</value></property>
-->
</bean>

View File

@@ -28,6 +28,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.net.SocketException;
import java.util.List;
import java.util.SortedMap;
import java.util.regex.Pattern;
@@ -36,6 +37,7 @@ import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.web.scripts.RepoStore;
import org.alfresco.service.cmr.avm.AVMExistsException;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMNotFoundException;
@@ -43,6 +45,9 @@ import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.web.scripts.Status;
import org.alfresco.web.scripts.WebScriptException;
@@ -62,7 +67,7 @@ public class AVMRemoteStore extends BaseRemoteStore
{
private static final Log logger = LogFactory.getLog(AVMRemoteStore.class);
private String rootPath;
private String rootPath = "/";
private AVMService avmService;
private SearchService searchService;
@@ -72,6 +77,11 @@ public class AVMRemoteStore extends BaseRemoteStore
*/
public void setRootPath(String rootPath)
{
if(rootPath != null && rootPath.length() == 0)
{
rootPath = "/";
}
this.rootPath = rootPath;
}
@@ -94,13 +104,14 @@ public class AVMRemoteStore extends BaseRemoteStore
/**
* Gets the last modified timestamp for the document.
*
* @param store the store id
* @param path document path to an existing document
*/
@Override
protected void lastModified(WebScriptResponse res, String path)
protected void lastModified(WebScriptResponse res, String store, String path)
throws IOException
{
String avmPath = buildAVMPath(path);
String avmPath = buildAVMPath(store, path);
AVMNodeDescriptor desc = this.avmService.lookup(-1, avmPath);
if (desc == null)
{
@@ -116,9 +127,9 @@ public class AVMRemoteStore extends BaseRemoteStore
* @see org.alfresco.repo.web.scripts.bean.BaseRemoteStore#getDocument(org.alfresco.web.scripts.WebScriptResponse, java.lang.String)
*/
@Override
protected void getDocument(final WebScriptResponse res, final String path) throws IOException
protected void getDocument(final WebScriptResponse res, final String store, final String path) throws IOException
{
final String avmPath = buildAVMPath(path);
final String avmPath = buildAVMPath(store, path);
final AVMNodeDescriptor desc = this.avmService.lookup(-1, avmPath);
if (desc == null)
{
@@ -201,9 +212,9 @@ public class AVMRemoteStore extends BaseRemoteStore
* @see org.alfresco.repo.web.scripts.bean.BaseRemoteStore#hasDocument(org.alfresco.web.scripts.WebScriptResponse, java.lang.String)
*/
@Override
protected void hasDocument(WebScriptResponse res, String path) throws IOException
protected void hasDocument(WebScriptResponse res, String store, String path) throws IOException
{
String avmPath = buildAVMPath(path);
String avmPath = buildAVMPath(store, path);
AVMNodeDescriptor desc = this.avmService.lookup(-1, avmPath);
Writer out = res.getWriter();
@@ -215,14 +226,14 @@ public class AVMRemoteStore extends BaseRemoteStore
* @see org.alfresco.repo.web.scripts.bean.BaseRemoteStore#createDocument(org.alfresco.web.scripts.WebScriptResponse, java.lang.String, java.io.InputStream)
*/
@Override
protected void createDocument(final WebScriptResponse res, 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>()
{
@SuppressWarnings("synthetic-access")
public Object doWork() throws Exception
{
String avmPath = buildAVMPath(path);
String avmPath = buildAVMPath(store, path);
try
{
String[] parts = AVMNodeConverter.SplitBase(avmPath);
@@ -241,6 +252,7 @@ public class AVMRemoteStore extends BaseRemoteStore
}
avmService.createFile(parts[0], parts[1], content);
avmService.createSnapshot(store, "AVMRemoteStore.createDocument()", path);
}
catch (AccessDeniedException ae)
{
@@ -259,9 +271,9 @@ public class AVMRemoteStore extends BaseRemoteStore
* @see org.alfresco.repo.web.scripts.bean.BaseRemoteStore#updateDocument(org.alfresco.web.scripts.WebScriptResponse, java.lang.String, java.io.InputStream)
*/
@Override
protected void updateDocument(final WebScriptResponse res, final String path, final InputStream content)
protected void updateDocument(final WebScriptResponse res, final String store, final String path, final InputStream content)
{
final String avmPath = buildAVMPath(path);
final String avmPath = buildAVMPath(store, path);
AVMNodeDescriptor desc = this.avmService.lookup(-1, avmPath);
if (desc == null)
{
@@ -292,9 +304,9 @@ public class AVMRemoteStore extends BaseRemoteStore
* @see org.alfresco.repo.web.scripts.bean.BaseRemoteStore#deleteDocument(org.alfresco.web.scripts.WebScriptResponse, java.lang.String)
*/
@Override
protected void deleteDocument(final WebScriptResponse res, final String path)
protected void deleteDocument(final WebScriptResponse res, final String store, final String path)
{
final String avmPath = buildAVMPath(path);
final String avmPath = buildAVMPath(store, path);
AVMNodeDescriptor desc = this.avmService.lookup(-1, avmPath);
if (desc == null)
{
@@ -310,6 +322,7 @@ public class AVMRemoteStore extends BaseRemoteStore
try
{
avmService.removeNode(avmPath);
avmService.createSnapshot(store, "AVMRemoteStore.deleteDocument()", path);
}
catch (AccessDeniedException ae)
{
@@ -324,9 +337,9 @@ public class AVMRemoteStore extends BaseRemoteStore
* @see org.alfresco.repo.web.scripts.bean.BaseRemoteStore#listDocuments(org.alfresco.web.scripts.WebScriptResponse, java.lang.String, boolean)
*/
@Override
protected void listDocuments(WebScriptResponse res, String path, boolean recurse) throws IOException
protected void listDocuments(WebScriptResponse res, String store, String path, boolean recurse) throws IOException
{
String avmPath = buildAVMPath(path);
String avmPath = buildAVMPath(store, path);
AVMNodeDescriptor node = this.avmService.lookup(-1, avmPath);
if (node == null)
{
@@ -336,7 +349,7 @@ public class AVMRemoteStore extends BaseRemoteStore
try
{
traverseNode(res.getWriter(), node, null, recurse);
traverseNode(res.getWriter(), store, node, recurse);
}
catch (AccessDeniedException ae)
{
@@ -348,13 +361,45 @@ public class AVMRemoteStore extends BaseRemoteStore
}
}
private void traverseNode(Writer out, String store, AVMNodeDescriptor node, boolean recurse)
throws IOException
{
/**
* The node path appears as such:
* project1:/www/avm_webapps/ROOT/WEB-INF/classes/alfresco/site-data/template-instances/file.xml
*/
int cropPoint = store.length() + this.rootPath.length() + 1;
SortedMap<String, AVMNodeDescriptor> listing = this.avmService.getDirectoryListing(node);
for (AVMNodeDescriptor n : listing.values())
{
if (n.isFile())
{
/*
String myPath = n.getPath();
String origPath = node.getPath();
String toWrite = myPath.substring(origPath.length());
out.write(toWrite);
out.write("\n");
*/
out.write(n.getPath().substring(cropPoint));
out.write("\n");
}
else if (recurse && n.isDirectory())
{
traverseNode(out, store, n, recurse);
}
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.web.scripts.bean.BaseRemoteStore#listDocuments(org.alfresco.web.scripts.WebScriptResponse, java.lang.String, java.lang.String)
*/
@Override
protected void listDocuments(WebScriptResponse res, String path, String pattern) throws IOException
protected void listDocuments(WebScriptResponse res, final String store, String path, String pattern) throws IOException
{
String avmPath = buildAVMPath(path);
String avmPath = buildAVMPath(store, path);
AVMNodeDescriptor node = this.avmService.lookup(-1, avmPath);
if (node == null)
{
@@ -368,70 +413,68 @@ public class AVMRemoteStore extends BaseRemoteStore
}
String matcher = pattern.replace(".","\\.").replace("*",".*");
final Pattern pat = Pattern.compile(matcher);
String encPath = RepoStore.encodePathISO9075(path);
final StringBuilder query = new StringBuilder(128);
query.append("+PATH:\"").append(this.rootPath)
.append(encPath.length() != 0 ? ('/' + encPath) : "")
.append("//*\" +QNAME:")
.append(pattern);
/*
query.append("+PATH:\"/").append(this.rootPath)
.append(encPath.length() != 0 ? ('/' + encPath) : "")
.append("//*\" +QNAME:")
.append(pattern);
*/
final Writer out = res.getWriter();
final StoreRef avmStore = new StoreRef(StoreRef.PROTOCOL_AVM + StoreRef.URI_FILLER + store);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
@SuppressWarnings("synthetic-access")
public Object doWork() throws Exception
{
int cropPoint = store.length() + rootPath.length() + 1;
ResultSet resultSet = searchService.query(avmStore, SearchService.LANGUAGE_LUCENE, query.toString());
try
{
traverseNode(res.getWriter(), node, Pattern.compile(matcher), true);
}
catch (AccessDeniedException ae)
List<NodeRef> nodes = resultSet.getNodeRefs();
for (NodeRef nodeRef : nodes)
{
res.setStatus(Status.STATUS_UNAUTHORIZED);
String path = AVMNodeConverter.ToAVMVersionPath(nodeRef).getSecond();
String name = path.substring(path.lastIndexOf('/') + 1);
if (pat.matcher(name).matches())
{
out.write(path.substring(cropPoint));
out.write("\n");
}
}
}
finally
{
res.getWriter().close();
resultSet.close();
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
/**
* @param store the store id
* @param path root path relative
*
* @return full AVM path to document including store and root path components
*/
private String buildAVMPath(String path)
private String buildAVMPath(String store, String path)
{
return this.store + ":/" + this.rootPath + (path != null ? ("/" + path) : "");
}
/**
* Traverse a Node and recursively output the file paths it contains.
*
* @param out Writer for output - relative paths separated by newline characters
* @param node The AVM Node to traverse
* @param pattern Optional Pattern to match filenames against
* @param recurse True to recurse sub-directories
*
* @throws IOException
*/
private void traverseNode(Writer out, AVMNodeDescriptor node, Pattern pattern, boolean recurse)
throws IOException
{
int cropPoint = this.store.length() + this.rootPath.length() + 3;
SortedMap<String, AVMNodeDescriptor> listing = this.avmService.getDirectoryListing(node);
for (AVMNodeDescriptor n : listing.values())
{
if (n.isFile())
{
String path = n.getPath();
if (pattern != null)
{
String name = path.substring(path.lastIndexOf('/') + 1);
if (pattern.matcher(name).matches())
{
out.write(path.substring(cropPoint));
out.write("\n");
}
}
else
{
out.write(path.substring(cropPoint));
out.write("\n");
}
}
else if (recurse && n.isDirectory())
{
traverseNode(out, n, pattern, recurse);
}
//return store + ":/" + this.rootPath + (path != null ? ("/" + path) : "");
if(path.startsWith("/"))
{
path = path.substring(1);
}
return store + ":" + this.rootPath + (path != null ? path : "");
}
}

View File

@@ -28,6 +28,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
@@ -49,6 +50,8 @@ import org.apache.commons.logging.LogFactory;
*
* Request format:
* <servicepath>/<method>/<path>[?<args>]
* <servicepath>/<method>/s/<store>/<path>[?<args>]
* <servicepath>/<method>/s/<store>/w/<webapp>/<path>[?<args>]
*
* Example:
* /service/remotestore/lastmodified/sites/xyz/pages/page.xml
@@ -58,6 +61,11 @@ import org.apache.commons.logging.LogFactory;
* /lastmodified -> method name
* /sites/../page.xml -> document path
*
* optional request parameters:
*
* s -> the avm store id
* w -> the wcm web application id
*
* Note: path is relative to the root path as configured for this webscript bean
*
* Further URL arguments may be provided if required by specific API methods.
@@ -81,19 +89,25 @@ import org.apache.commons.logging.LogFactory;
*/
public abstract class BaseRemoteStore extends AbstractWebScript
{
public static final String TOKEN_STORE = "s";
public static final String TOKEN_WEBAPP = "w";
public static final String REQUEST_PARAM_STORE = "s";
public static final String REQUEST_PARAM_WEBAPP = "w";
private static final Log logger = LogFactory.getLog(BaseRemoteStore.class);
protected String store;
protected String defaultStore;
protected ContentService contentService;
protected MimetypeService mimetypeService;
/**
* @param store the store name of the store to process document requests against
* @param defaultStore the default store name of the store to process document requests against
*/
public void setStore(String store)
public void setStore(String defaultStore)
{
this.store = store;
this.defaultStore = defaultStore;
}
/**
@@ -125,78 +139,155 @@ public abstract class BaseRemoteStore extends AbstractWebScript
HttpServletRequest httpReq = ((WebScriptServletRequest)req).getHttpServletRequest();
// break down and validate the request - expecting method name and document path
// the request path for the remote store
String extPath = req.getExtensionPath();
String[] extParts = extPath == null ? new String[0] : extPath.split("/");
if (extParts.length < 1)
// values that we need to determine
String methodName = null;
String store = null;
String webapp = null;
StringBuilder pathBuilder = null;
// tokenize the path and figure out tokenized values
StringTokenizer tokenizer = new StringTokenizer(extPath, "/");
if (tokenizer.hasMoreTokens())
{
throw new WebScriptException("Remote Store expecting method name.");
methodName = tokenizer.nextToken();
if (tokenizer.hasMoreTokens())
{
String el = tokenizer.nextToken();
if (TOKEN_STORE.equals(el))
{
// if the token is "s", then the next token is the id of the store
store = tokenizer.nextToken();
// reset el
el = (tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null);
}
// extract path from url extension
String path = null;
if (extParts.length >= 2)
if (TOKEN_WEBAPP.equals(el))
{
path = req.getExtensionPath().substring(extParts[0].length() + 1);
// if the token is "w", then the next token is a WCM webapp id
webapp = tokenizer.nextToken();
// reset el
el = (tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null);
}
while(el != null)
{
if(pathBuilder == null)
{
pathBuilder = new StringBuilder(128);
}
pathBuilder.append("/");
pathBuilder.append(el);
el = (tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null);
}
}
}
else
{
throw new WebScriptException("Unable to tokenize web path: " + extPath);
}
// if we don't have a store, check whether it came in on a request parameter
if (store == null)
{
store = req.getParameter(REQUEST_PARAM_STORE);
if (store == null)
{
store = this.defaultStore;
}
if (store == null)
{
// not good, we should have a store by this point
// this means that a store was not passed in and that we
// also didn't have a configured store
throw new WebScriptException("Unable to determine which store to operate against. A store was not specified and a default was not provided.");
}
}
// if we don't have a webapp, check whether it may have been passed in on a request parameter
if (webapp == null)
{
webapp = req.getParameter(REQUEST_PARAM_WEBAPP);
}
// if we do have a webapp, allow for path prepending
if (webapp != null)
{
pathBuilder.insert(0, "/www/avm_webapps/" + webapp);
}
// convert down to the path
String path = pathBuilder.toString();
// debugger information
if (logger.isDebugEnabled())
logger.debug("Remote store method: " + extParts[0] + " path: " + path);
// TODO: support storeref name override as argument? (i.e. for AVM virtualisation)
{
logger.debug("Remote method: " + methodName);
logger.debug("Remote store id: " + store);
logger.debug("Remote path: " + path);
}
try
{
// generate enum from string method name - so we can use a fast switch table lookup
APIMethod method = APIMethod.valueOf(extParts[0].toUpperCase());
APIMethod method = APIMethod.valueOf(methodName.toUpperCase());
switch (method)
{
case LASTMODIFIED:
validatePath(path);
lastModified(res, path);
lastModified(res, store, path);
break;
case HAS:
validatePath(path);
hasDocument(res, path);
hasDocument(res, store, path);
break;
case GET:
validatePath(path);
getDocument(res, path);
getDocument(res, store, path);
break;
case LIST:
listDocuments(res, path, false);
listDocuments(res, store, path, false);
break;
case LISTALL:
listDocuments(res, path, true);
listDocuments(res, store, path, true);
break;
case LISTPATTERN:
listDocuments(res, path, req.getParameter("m"));
listDocuments(res, store, path, req.getParameter("m"));
break;
case CREATE:
validatePath(path);
createDocument(res, path, httpReq.getInputStream());
createDocument(res, store, path, httpReq.getInputStream());
break;
case UPDATE:
validatePath(path);
updateDocument(res, path, httpReq.getInputStream());
updateDocument(res, store, path, httpReq.getInputStream());
break;
case DELETE:
validatePath(path);
deleteDocument(res, path);
deleteDocument(res, store, path);
break;
}
}
catch (IllegalArgumentException enumErr)
{
throw new WebScriptException("Unknown method specified to remote store API: " + extParts[0]);
throw new WebScriptException("Unknown method specified to remote store API: " + methodName);
}
catch (IOException ioErr)
{
@@ -233,9 +324,10 @@ public abstract class BaseRemoteStore extends AbstractWebScript
*
* The output will be the last modified date as a long toString().
*
* @param store the store id
* @param path document path to an existing document
*/
protected abstract void lastModified(WebScriptResponse res, String path)
protected abstract void lastModified(WebScriptResponse res, String store, String path)
throws IOException;
/**
@@ -243,9 +335,10 @@ public abstract class BaseRemoteStore extends AbstractWebScript
*
* The output will be either the string "true" or the string "false".
*
* @param store the store id
* @param path document path
*/
protected abstract void hasDocument(WebScriptResponse res, String path)
protected abstract void hasDocument(WebScriptResponse res, String store, String path)
throws IOException;
/**
@@ -253,12 +346,13 @@ public abstract class BaseRemoteStore extends AbstractWebScript
*
* The output will be the document content stream.
*
* @param store the store id
* @param path document path
* @return
*
* @throws IOException if an error occurs retrieving the document
*/
protected abstract void getDocument(WebScriptResponse res, String path)
protected abstract void getDocument(WebScriptResponse res, String store, String path)
throws IOException;
/**
@@ -267,12 +361,13 @@ public abstract class BaseRemoteStore extends AbstractWebScript
* The output will be the list of relative document paths found under the path.
* Separated by newline characters.
*
* @param store the store id
* @param path document path
* @param recurse true to peform a recursive list, false for direct children only.
*
* @throws IOException if an error occurs listing the documents
*/
protected abstract void listDocuments(WebScriptResponse res, String path, boolean recurse)
protected abstract void listDocuments(WebScriptResponse res, String store, String path, boolean recurse)
throws IOException;
/**
@@ -281,42 +376,46 @@ public abstract class BaseRemoteStore extends AbstractWebScript
* The output will be the list of relative document paths found under the path that
* match the given file pattern. Separated by newline characters.
*
* @param store the store id
* @param path document path
* @param pattern file pattern to match - allows wildcards e.g. *.xml or site*.xml
*
* @throws IOException if an error occurs listing the documents
*/
protected abstract void listDocuments(WebScriptResponse res, String path, String pattern)
protected abstract void listDocuments(WebScriptResponse res, String store, String path, String pattern)
throws IOException;
/**
* Creates a document.
*
* @param store the store id
* @param path document path
* @param content content of the document to write
*
* @throws IOException if the create fails
*/
protected abstract void createDocument(WebScriptResponse res, String path, InputStream content);
protected abstract void createDocument(WebScriptResponse res, String store, String path, InputStream content);
/**
* Updates an existing document.
*
* @param store the store id
* @param path document path
* @param content content to update the document with
*
* @throws IOException if the update fails
*/
protected abstract void updateDocument(WebScriptResponse res, String path, InputStream content);
protected abstract void updateDocument(WebScriptResponse res, String store, String path, InputStream content);
/**
* Deletes an existing document.
*
* @param store the store id
* @param path document path
*
* @throws IOException if the delete fails
*/
protected abstract void deleteDocument(WebScriptResponse res, String path);
protected abstract void deleteDocument(WebScriptResponse res, String store, String path);
/**