Skip to content

Commit

Permalink
MODE-806 RESTful server should allow queries to be executed
Browse files Browse the repository at this point in the history
Applied a patch that adds a new URI pattern (/<context root>/<repository name>/<workspace name>/query) for query support.  Users can execute a query by POSTing a request to that URI with the unencoded query in the body of the message and a content type that specifies which language to use.  The REST server will then execute that query in the workspace described by the URI.

The query results will be returned as a JSON-encoded object with two properties: types, which maps the column names in the query results to their JCR type, and rows, which contains a JSON-encoded array of rows.  Each element in the rows array corresponds to a single row in the query results and each element is a JSON object that maps column names to their value for that particular row.  The types property relies on ModeShape-specific functionality and will not contain any mappings if the REST server is configured to use another JCR implementation.

This patch also updates the REST client to add a new query method that returns a list of QueryRow objects.  Each QueryRow provides a collection of column names in the row, the t value for a named column, and the type for a named column.  The type of the column will always be null if a JCR implementation other than ModeShape is used.

This patch includes Randall's suggestion to perform case-insensitive compares in the JsonRestClient when determining the query language.

git-svn-id: https://svn.jboss.org/repos/modeshape/trunk@2075 76366958-4244-0410-ad5e-bbfabb93f86b
  • Loading branch information
bcarothers-xx committed Aug 1, 2010
1 parent c2b2ebd commit 7bb33ba
Show file tree
Hide file tree
Showing 26 changed files with 1,144 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@
/tools/org.modeshape.eclipse.jcr.rest.client/bin

# Directory created by Eclipse
/RemoteSystemsTempFiles
/RemoteSystemsTempFiles
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public void shouldServeListOfWorkspacesForValidRepository() throws Exception {

JSONObject objFromResponse = new JSONObject(body);
JSONObject expected = new JSONObject(
"{\"default\":{\"workspace\":{\"name\":\"default\",\"resources\":{\"items\":\"/modeshape/mode%3arepository/default/items\"}}}}");
"{\"default\":{\"workspace\":{\"name\":\"default\",\"resources\":{\"query\":\"/modeshape/mode%3arepository/default/query\",\"items\":\"/modeshape/mode%3arepository/default/items\"}}}}");

assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
assertThat(objFromResponse.toString(), is(expected.toString()));
Expand Down
96 changes: 93 additions & 3 deletions docs/reference/src/main/docbook/en-US/content/jcr/web_access.xml
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/ma
<row>
<entry>/resources/{repositoryName}/{workspaceName}/item/{path}</entry>
<entry>Accesses the item (node or property) at the path</entry>
<entry>ALL</entry>
<entry>GET, POST, PUT, DELETE</entry>
</row>
<row>
<entry>/resources/{repositoryName}/{workspaceName}/query</entry>
<entry>Executes the query in the request body</entry>
<entry>POST</entry>
</row>
</tbody>
</tgroup>
Expand Down Expand Up @@ -495,15 +500,19 @@ GET http://www.example.com/resources/modeshape%3arepository
"default" : {
"workspace" : {
"name" : "default",
"resources" : { "items":"/resources/modeshape%3arepository/default/items" }
"resources" : {
"items":"/resources/modeshape%3arepository/default/items",
"query":"/resources/modeshape%3arepository/default/query"
},
}
}
}
]]></programlisting>
Like the first response, this response consists of a list of workspace names mapped to metadata about the
workspaces. The example above only lists one workspace for simplicity, but there could be many different
workspaces returned in a real deployment. Note that the "items" resource builds the full URI to the root
of the items hierarchy, including the encoding of the repository name and the workspace name.
of the items hierarchy, including the encoding of the repository name and the workspace name and the "query"
resource builds the full URI needed to execute queries.
</para>
<para>
Now a request can be built to retrieve the root item of the repository.
Expand Down Expand Up @@ -607,6 +616,87 @@ PUT http://www.example.com/resources/modeshape%3arepository/default/items/newNod
</para>
</note>
</para>


<para>
Queries can be executed through the REST interface by POSTing to the query URI with the query statement in the
body of the request. The query language <emphasis>must</emphasis> be specified by setting the appropriate MIME
type.
<table frame='all'>
<title>Query Content Types for the ModeShape REST Server</title>
<tgroup cols='2' align='left' colsep='1' rowsep='1'>
<colspec colname='language' colwidth="3*"/>
<colspec colname='content type' colwidth="1*"/>
<thead>
<row>
<entry>Query Language</entry>
<entry>Content Type</entry>
</row>
</thead>
<tbody>
<row>
<entry>XPath</entry>
<entry>application/jcr+xpath</entry>
</row>
<row>
<entry>JCR-SQL</entry>
<entry>application/jcr+sql</entry>
</row>
<row>
<entry>JCR-SQL2</entry>
<entry>application/jcr+sql2</entry>
</row>
<row>
<entry>Full Text Search</entry>
<entry>application/jcr+search</entry>
</row>
</tbody>
</tgroup>
</table>
If no content type is specified or the content type for the request is not one of the content types listed
above, the request will generate a response code of 400 (BAD REQUEST).
</para><para>
All queries for a given workspace are posted to the same URI and the request body is not JSON-encoded.
<programlisting><![CDATA[
POST http://www.example.com/resources/modeshape%3arepository/default/query
/a/b/c/d[@foo='bar']
]]></programlisting>
Assuming that the request above was POSTed with a content type of <code>application/jcr+xpath</code>, a
response would be generated that consisted of a JSON object that contained a property named "rows". The "rows"
property would contain an array of rows with each element being a JSON object that represented one row
in the query result set.
</para>
<programlisting><![CDATA[
{
"types": {
"someProperty": "STRING",
"someOtherProperty": "BOOLEAN",
"jcr:path": "STRING",
"jcr:score": "DECIMAL"
},
"rows": {
{
"someProperty": "foobar",
"someOtherProperty": "true",
"jcr:path" : "/a/b/c/d",
"jcr:score" : 0.9327
},
{
"someProperty": "localValue",
"someOtherProperty": "false",
"jcr:path" : "/a/b/c/d[2]",
"jcr:score" : 0.8143
}
}
}
]]></programlisting>
<para>
If ModeShape is used as the underlying JCR implementation, the JSON object in the response will
also contain a "types" property. The value of the "types" property is a JSON object that maps
column names to their JCR type.
</para>

<sect3 id="binary_properties_in_rest_representations">
<title>Binary properties</title>
<para>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
*/
package org.modeshape.web.jcr.rest.client;

import java.util.Arrays;
import java.util.List;

/**
* The <code>IJcrContants</code> class provides constants for the commonly used JCR types and property identifiers.
*/
Expand Down Expand Up @@ -68,4 +71,30 @@ public interface IJcrConstants {
*/
String RESOURCE_NODE_TYPE = "nt:resource";

/**
* The query language value for XPath queries
*/
String XPATH = "xpath";

/**
* The query language value for JCR-SQL queries
*/
String JCR_SQL = "sql";

/**
* The query language value for JCR-SQL2 queries
*/
String JCR_SQL2 = "JCR-SQL2";

/**
* The query language value for full text search queries
*/
String JCR_SEARCH = "Search";

/**
* A list of the valid query languages
*/
List<String> VALID_QUERY_LANGUAGES = Arrays.asList(new String[] {IJcrConstants.XPATH, IJcrConstants.JCR_SQL,
IJcrConstants.JCR_SQL2, IJcrConstants.JCR_SEARCH});

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import java.io.File;
import java.net.URL;
import java.util.Collection;
import java.util.List;
import org.modeshape.web.jcr.rest.client.Status.Severity;
import org.modeshape.web.jcr.rest.client.domain.QueryRow;
import org.modeshape.web.jcr.rest.client.domain.Repository;
import org.modeshape.web.jcr.rest.client.domain.Server;
import org.modeshape.web.jcr.rest.client.domain.Workspace;
Expand Down Expand Up @@ -90,4 +92,33 @@ Status unpublish( Workspace workspace,
String path,
File file );

/**
* Executes the given query in the workspace.
*
* @param workspace the workspace where the resource will be unpublished (never <code>null</code>)
* @param language the JCR query language to use (never <code>null</code>)
* @param statement the query itself (never <code>null</code>)
* @return the list of rows returned by the query (never <code>null</code>)
* @throws Exception if there is a problem obtaining the workspaces
*/
List<QueryRow> query( Workspace workspace,
String language,
String statement ) throws Exception;

/**
* Executes the given query in the workspace.
*
* @param workspace the workspace where the resource will be unpublished (never <code>null</code>)
* @param language the JCR query language to use (never <code>null</code>)
* @param statement the query itself (never <code>null</code>)
* @param offset the first row to be returned; if this value is negative, rows are returned starting with the first row
* @param limit the maximum number of rows to be returned; if this value is negative, all rows are returned
* @return the list of rows returned by the query (never <code>null</code>)
* @throws Exception if there is a problem obtaining the workspaces
*/
List<QueryRow> query( Workspace workspace,
String language,
String statement,
int offset,
int limit ) throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public final class RestClientI18n {

public static I18n unpublishSucceededMsg;

public static I18n invalidQueryLanguageMsg;

static {
try {
I18n.initialize(RestClientI18n.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.modeshape.web.jcr.rest.client.domain;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import net.jcip.annotations.Immutable;

@Immutable
public class QueryRow {

private Map<String, String> queryTypes;
private Map<String, Object> values;

public QueryRow( Map<String, String> queryTypes,
Map<String, Object> values ) {
super();
// queryTypes is expected to already be an unmodifiable map
this.queryTypes = queryTypes;
this.values = Collections.unmodifiableMap(values);
}

public Collection<String> getColumnNames() {
return values.keySet();
}

public Object getValue( String columnName ) {
return values.get(columnName);
}

public String getColumnType( String columnName ) {
return queryTypes.get(columnName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ public final class HttpClientConnection {
*/
private HttpResponse response;

/**
* The content type
*/
private String contentType;

// ===========================================================================================================================
// Constructors
// ===========================================================================================================================
Expand Down Expand Up @@ -152,11 +157,22 @@ public void write( byte[] bytes ) throws Exception {
CheckArg.isNotNull(bytes, "bytes");

ByteArrayEntity entity = new ByteArrayEntity(bytes);
entity.setContentType(MediaType.APPLICATION_JSON);
if (contentType == null) {
entity.setContentType(MediaType.APPLICATION_JSON);
}

if (this.request instanceof HttpEntityEnclosingRequestBase) {
((HttpEntityEnclosingRequestBase)this.request).setEntity(entity);
}
}

/**
* Sets the content type for the request
*
* @param contentType the content type to use
*/
public void setContentType( String contentType ) {
this.contentType = contentType;
this.request.setHeader("Content-Type", contentType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,14 @@ enum RequestMethod {
*/
String WORKSPACE_CONTEXT = "/items";

/**
* The segment added to the URLs for queries.
*/
String QUERY_CONTEXT = "/query";

/**
* The suffix appended to properties whose values are base64-encoded
*/
String BASE64_SUFFIX = "/base64/";

}
Loading

0 comments on commit 7bb33ba

Please sign in to comment.