
org.modeshape.web.jcr.rest.JcrResources Maven / Gradle / Ivy
Show all versions of modeshape-web-jcr-rest
/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.web.jcr.rest;
import java.io.IOException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.Query;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.codehaus.jettison.json.JSONException;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.util.Base64;
import org.modeshape.web.jcr.rest.handler.ItemHandler;
import org.modeshape.web.jcr.rest.handler.QueryHandler;
import org.modeshape.web.jcr.rest.handler.RepositoryHandler;
import org.modeshape.web.jcr.rest.handler.ServerHandler;
/**
* RESTEasy handler to provide the JCR resources at the URIs below. Please note that these URIs assume a context of
* {@code /resources} for the web application.
*
*
* URI Pattern
* Description
* Supported Methods
*
*
* /resources
* returns a list of accessible repositories
* GET
*
*
* /resources/{repositoryName}
* returns a list of accessible workspaces within that repository
* GET
*
*
* /resources/{repositoryName}/{workspaceName}
* returns a list of operations within the workspace
* GET
*
*
* /resources/{repositoryName}/{workspaceName}/items/{path}
* accesses the item (node or property) at the path
* GET, POST, PUT, DELETE
*
*
* /resources/{repositoryName}/{workspaceName}/query
* executes the query in the body of the request with a language specified by the content type (application/jcr+xpath,
* application/jcr+sql, application/jcr+sql2, or application/search)
* POST
*
*
* Binary data
*
* There are several ways to transfer binary property values, but all involve encoding the binary value into ASCII characters
* using a {@link Base64} notation and denoting this by adding annotating the property name with a suffix defining the type of
* encoding. Currently, only "base64" encoding is supported.
*
*
* For example, if the "jcr:data" property contains a single binary value of "propertyValue", then the JSON object representing
* that property will be:
*
*
* "jcr:data/base64/" : "cHJvcGVydHlWYWx1ZQ=="
*
*
* Likewise, if the "jcr:data" property contains two binary values each being "propertyValue", then the JSON object representing
* that property will be:
*
*
* "jcr:data/base64/" : [ "cHJvcGVydHlWYWx1ZQ==", "cHJvcGVydHlWYWx1ZQ==" ]
*
*
* Note that JCR 1.0.1 does not allow property names to and with a '/' character (among others), while JCR 2.0 does not allow
* property names to contain an unescaped or unencoded '/' character. Therefore, the "/{encoding}/" suffix can never appear in a
* valid JCR property name, and will always identify an encoded property.
*
*
* Here are the details:
*
* - Getting a node with
GET /resources/{repositoryName}/item/{pathToNode}
obtains the JSON object representing the
* node, and each property is represented as a nested JSON object where the name is the property name and the value(s) are
* represented as either a single string value or an array of string values. If the property has a binary value, then the property
* name is appended with "/base64/" and the string representation of each value is encoded in Base64.
* - Getting a property with
GET /resources/{repositoryName}/item/{pathToProperty}
allows only the value(s) for the
* one property to be included in the response. If any of the values is a binary value, then all of the values will be
* encoded in Base64.
* - Setting a property with
PUT /resources/{repositoryName}/item/{pathToProperty}
allows setting the property to a
* single value, and only that value needs to be included in the body of the request. If the value is binary, the value
* must be {@link Base64 encoded} by the client and the "Content-Transfer-Encoding" header must be set to "base64" (case
* does not matter). When the request is received, the value is decoded before the property value is updated on the node.
* - Creating a node with
POST /resources/{repositoryName}/item/{pathToNode}
requires a request that is structured
* in the same way as the response from getting a node: the resulting JSON object represents the node, with nested JSON objects
* for the properties and children. If any property of the new node has a binary value, then the name of the property must
* be appended with "/base64/" and the string representation of each value are to be encoded in Base64.
* - Updating a node with
PUT /resources/{repositoryName}/item/{pathToNode}
requires a request that is structured
* in the same way as the response from getting or posting a node: the resulting JSON object represents the node, with nested JSON
* objects for the properties and children. If any property of the new node has a binary value, then the name of the property
* must be appended with "/base64/" and the string representation of each value are to be encoded in Base64.
*
*
*
* @deprecated since 3.0, the default service is {@link ModeShapeRestService}
*/
@Deprecated
@Immutable
@Path( "/v1" )
public class JcrResources {
/**
* This is a duplicate of the FullTextSearchParser.LANGUAGE field, but it is split out here to avoid adding a dependency on
* the modeshape-jcr package.
*/
private final static String SEARCH_LANGUAGE = "Search";
private ServerHandler serverHandler = new ServerHandler();
private RepositoryHandler repositoryHandler = new RepositoryHandler();
private ItemHandler itemHandler = new ItemHandler();
private QueryHandler queryHandler = new QueryHandler();
/**
* Returns the list of JCR repositories available on this server
*
* @param request the servlet request; may not be null
* @return the list of JCR repositories available on this server
* @throws JSONException if there is an error encoding the node
* @throws RepositoryException if any other error occurs
*/
@GET
@Path( "/" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String getRepositories( @Context HttpServletRequest request ) throws JSONException, RepositoryException {
return serverHandler.getRepositories(request);
}
/**
* Returns the list of workspaces available to this user within the named repository.
*
* @param rawRepositoryName the name of the repository; may not be null
* @param request the servlet request; may not be null
* @return the list of workspaces available to this user within the named repository.
* @throws JSONException if there is an error encoding the node
* @throws RepositoryException if there is any other error accessing the list of available workspaces for the repository
*/
@GET
@Path( "{repositoryName}" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String getWorkspaces( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName )
throws JSONException, RepositoryException {
return repositoryHandler.getWorkspaces(request, rawRepositoryName);
}
/**
* Handles GET requests for an item in a workspace.
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param path the path to the item
* @param deprecatedDepth the old depth parameter ("mode:depth"). This version is deprecated and should use the "depth" query
* parameter instead.
* @param depth the depth of the node graph that should be returned if {@code path} refers to a node. @{code 0} means return
* the requested node only. A negative value indicates that the full subgraph under the node should be returned. This
* parameter defaults to {@code 0} and is ignored if {@code path} refers to a property.
* @return the JSON-encoded version of the item (and, if the item is a node, its subgraph, depending on the value of
* {@code depth})
* @throws NotFoundException if the named repository does not exists, the named workspace does not exist, or the user does not
* have access to the named workspace
* @throws JSONException if there is an error encoding the node
* @throws NotAuthorizedException if the given login information is invalid
* @throws RepositoryException if any other error occurs
* @see org.modeshape.web.jcr.rest.handler.AbstractHandler#EMPTY_REPOSITORY_NAME
* @see org.modeshape.web.jcr.rest.handler.AbstractHandler#EMPTY_REPOSITORY_NAME
* @see Session#getItem(String)
*/
@GET
@Path( "{repositoryName}/{workspaceName}/items{path:.*}" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String getItem( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@PathParam( "path" ) String path,
@QueryParam( "mode:depth" ) @DefaultValue( "0" ) int deprecatedDepth,
@QueryParam( "depth" ) @DefaultValue( "0" ) int depth )
throws JSONException, NotAuthorizedException, RepositoryException {
if (depth == 0 && deprecatedDepth != 0) {
depth = deprecatedDepth;
}
return itemHandler.getItem(request, rawRepositoryName, rawWorkspaceName, path, depth);
}
/**
* Adds the content of the request as a node (or subtree of nodes) at the location specified by {@code path}.
*
* The primary type and mixin type(s) may optionally be specified through the {@code jcr:primaryType} and
* {@code jcr:mixinTypes} properties.
*
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param path the path to the item
* @param fullNodeInResponse if {@code fullNodeInResponse == null || Boolean.valueOf(fullNodeInResponse)}, indicates that a
* representation of the created node (including all properties and children) should be returned; otherwise, only the
* path to the new node will be returned
* @param requestContent the JSON-encoded representation of the node or nodes to be added
* @return the JSON-encoded representation of the node or nodes that were added. This will differ from {@code requestContent}
* in that auto-created and protected properties (e.g., jcr:uuid) will be populated.
* @throws NotFoundException if the parent of the item to be added does not exist
* @throws NotAuthorizedException if the user does not have the access required to create the node at this path
* @throws JSONException if there is an error encoding the node
* @throws RepositoryException if any other error occurs
*/
@POST
@Path( "{repositoryName}/{workspaceName}/items/{path:.*}" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public Response postItem( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@PathParam( "path" ) String path,
@QueryParam( "mode:includeNode" ) String fullNodeInResponse,
String requestContent )
throws NotFoundException, NotAuthorizedException, RepositoryException, JSONException {
return itemHandler.postItem(request,
rawRepositoryName,
rawWorkspaceName,
path,
fullNodeInResponse == null || Boolean.valueOf(fullNodeInResponse),
requestContent);
}
/**
* Deletes the item at {@code path}.
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param path the path to the item
* @throws NotFoundException if no item exists at {@code path}
* @throws NotAuthorizedException if the user does not have the access required to delete the item at this path
* @throws RepositoryException if any other error occurs
*/
@DELETE
@Path( "{repositoryName}/{workspaceName}/items{path:.*}" )
public void deleteItem( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@PathParam( "path" ) String path )
throws NotFoundException, NotAuthorizedException, RepositoryException {
itemHandler.deleteItem(request, rawRepositoryName, rawWorkspaceName, path);
}
/**
* Updates the properties at the path.
*
* If path points to a property, this method expects the request content to be either a JSON array or a JSON string. The array
* or string will become the values or value of the property. If path points to a node, this method expects the request
* content to be a JSON object. The keys of the objects correspond to property names that will be set and the values for the
* keys correspond to the values that will be set on the properties.
*
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param path the path to the item
* @param requestContent the JSON-encoded representation of the values and, possibly, properties to be set
* @return the JSON-encoded representation of the node on which the property or properties were set.
* @throws NotFoundException if the parent of the item to be added does not exist
* @throws NotAuthorizedException if the user does not have the access required to create the node at this path
* @throws JSONException if there is an error encoding the node
* @throws RepositoryException if any other error occurs
* @throws IOException if there is a problem reading the value
*/
@PUT
@Path( "{repositoryName}/{workspaceName}/items{path:.*}" )
@Consumes( MediaType.APPLICATION_JSON )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String putItem( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@PathParam( "path" ) String path,
String requestContent ) throws NotAuthorizedException, JSONException, RepositoryException, IOException {
return itemHandler.putItem(request, rawRepositoryName, rawWorkspaceName, path, requestContent);
}
/**
* Executes the XPath query contained in the body of the request against the give repository and workspace.
*
* The query results will be JSON-encoded in the response body.
*
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param offset the offset to the first row to be returned. If this value is greater than the size of the result set, no
* records will be returned. If this value is less than 0, results will be returned starting from the first record in
* the result set.
* @param limit the maximum number of rows to be returned. If this value is greater than the size of the result set, the
* entire result set will be returned. If this value is less than zero, the entire result set will be returned. The
* results are counted from the record specified in the offset parameter.
* @param uriInfo the information about the URI (from which the other query parameters will be obtained)
* @param requestContent the query expression
* @return the JSON-encoded representation of the query results.
* @throws JSONException if there is an error encoding the node
* @throws InvalidQueryException if the query contained an error, was invalid, or could not be executed
* @throws RepositoryException if any other error occurs
*/
@POST
@Path( "{repositoryName}/{workspaceName}/query" )
@Consumes( "application/jcr+xpath" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String postXPathQuery( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@QueryParam( "offset" ) @DefaultValue( "-1" ) long offset,
@QueryParam( "limit" ) @DefaultValue( "-1" ) long limit,
@Context UriInfo uriInfo,
String requestContent ) throws InvalidQueryException, RepositoryException, JSONException {
return queryHandler.postItem(request,
rawRepositoryName,
rawWorkspaceName,
Query.XPATH,
requestContent,
offset,
limit,
uriInfo);
}
/**
* Executes the JCR-SQL query contained in the body of the request against the give repository and workspace.
*
* The query results will be JSON-encoded in the response body.
*
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param offset the offset to the first row to be returned. If this value is greater than the size of the result set, no
* records will be returned. If this value is less than 0, results will be returned starting from the first record in
* the result set.
* @param limit the maximum number of rows to be returned. If this value is greater than the size of the result set, the
* entire result set will be returned. If this value is less than zero, the entire result set will be returned. The
* results are counted from the record specified in the offset parameter.
* @param uriInfo the information about the URI (from which the other query parameters will be obtained)
* @param requestContent the query expression
* @return the JSON-encoded representation of the query results.
* @throws JSONException if there is an error encoding the node
* @throws InvalidQueryException if the query contained an error, was invalid, or could not be executed
* @throws RepositoryException if any other error occurs
*/
@POST
@Path( "{repositoryName}/{workspaceName}/query" )
@Consumes( "application/jcr+sql" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String postJcrSqlQuery( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@QueryParam( "offset" ) @DefaultValue( "-1" ) long offset,
@QueryParam( "limit" ) @DefaultValue( "-1" ) long limit,
@Context UriInfo uriInfo,
String requestContent ) throws InvalidQueryException, RepositoryException, JSONException {
return queryHandler.postItem(request,
rawRepositoryName,
rawWorkspaceName,
Query.SQL,
requestContent,
offset,
limit,
uriInfo);
}
/**
* Executes the JCR-SQL2 query contained in the body of the request against the give repository and workspace.
*
* The query results will be JSON-encoded in the response body.
*
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param offset the offset to the first row to be returned. If this value is greater than the size of the result set, no
* records will be returned. If this value is less than 0, results will be returned starting from the first record in
* the result set.
* @param limit the maximum number of rows to be returned. If this value is greater than the size of the result set, the
* entire result set will be returned. If this value is less than zero, the entire result set will be returned. The
* results are counted from the record specified in the offset parameter.
* @param uriInfo the information about the URI (from which the other query parameters will be obtained)
* @param requestContent the query expression
* @return the JSON-encoded representation of the query results.
* @throws JSONException if there is an error encoding the node
* @throws InvalidQueryException if the query contained an error, was invalid, or could not be executed
* @throws RepositoryException if any other error occurs
*/
@POST
@Path( "{repositoryName}/{workspaceName}/query" )
@Consumes( "application/jcr+sql2" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String postJcrSql2Query( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@QueryParam( "offset" ) @DefaultValue( "-1" ) long offset,
@QueryParam( "limit" ) @DefaultValue( "-1" ) long limit,
@Context UriInfo uriInfo,
String requestContent ) throws InvalidQueryException, RepositoryException, JSONException {
return queryHandler.postItem(request,
rawRepositoryName,
rawWorkspaceName,
Query.JCR_SQL2,
requestContent,
offset,
limit,
uriInfo);
}
/**
* Executes the JCR-SQL query contained in the body of the request against the give repository and workspace.
*
* The query results will be JSON-encoded in the response body.
*
*
* @param request the servlet request; may not be null or unauthenticated
* @param rawRepositoryName the URL-encoded repository name
* @param rawWorkspaceName the URL-encoded workspace name
* @param offset the offset to the first row to be returned. If this value is greater than the size of the result set, no
* records will be returned. If this value is less than 0, results will be returned starting from the first record in
* the result set.
* @param limit the maximum number of rows to be returned. If this value is greater than the size of the result set, the
* entire result set will be returned. If this value is less than zero, the entire result set will be returned. The
* results are counted from the record specified in the offset parameter.
* @param uriInfo the information about the URI (from which the other query parameters will be obtained)
* @param requestContent the query expression
* @return the JSON-encoded representation of the query results.
* @throws JSONException if there is an error encoding the node
* @throws InvalidQueryException if the query contained an error, was invalid, or could not be executed
* @throws RepositoryException if any other error occurs
*/
@POST
@Path( "{repositoryName}/{workspaceName}/query" )
@Consumes( "application/jcr+search" )
@Produces( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML} )
public String postJcrSearchQuery( @Context HttpServletRequest request,
@PathParam( "repositoryName" ) String rawRepositoryName,
@PathParam( "workspaceName" ) String rawWorkspaceName,
@QueryParam( "offset" ) @DefaultValue( "-1" ) long offset,
@QueryParam( "limit" ) @DefaultValue( "-1" ) long limit,
@Context UriInfo uriInfo,
String requestContent ) throws RepositoryException, JSONException {
return queryHandler.postItem(request,
rawRepositoryName,
rawWorkspaceName,
SEARCH_LANGUAGE,
requestContent,
offset,
limit,
uriInfo);
}
}