HttpRemoteJsonResource.java revision 81e0baf43f70767a19dd915727f79d142af56a7b
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk/*
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk *
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * Copyright (c) 2011-2013 ForgeRock AS. All Rights Reserved
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk *
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * The contents of this file are subject to the terms
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * of the Common Development and Distribution License
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * (the License). You may not use this file except in
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * compliance with the License.
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk *
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * You can obtain a copy of the License at
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * http://forgerock.org/license/CDDLv1.0.html
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * See the License for the specific language governing
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * permission and limitations under the License.
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk *
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * When distributing Covered Code, include this CDDL
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * Header Notice in each file and include the License file
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * at http://forgerock.org/license/CDDLv1.0.html
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * If applicable, add the following below the CDDL Header,
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * with the fields enclosed by brackets [] replaced by
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * your own identifying information:
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * "Portions Copyrighted [year] [name of copyright owner]"
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk */
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkpackage org.forgerock.openidm.shell.impl;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.io.IOException;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.util.ArrayList;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.util.Arrays;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.util.Collection;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.util.HashMap;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.util.List;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.util.Map;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport java.util.logging.Level;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.codehaus.jackson.map.ObjectMapper;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.fluent.JsonValue;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.fluent.JsonValueException;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.ActionRequest;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.BadRequestException;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.Connection;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.CreateRequest;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.DeleteRequest;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.FutureResult;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.InternalServerErrorException;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.PatchOperation;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.PatchRequest;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.QueryRequest;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.QueryResult;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.QueryResultHandler;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.ReadRequest;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.Request;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.Resource;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.ResultHandler;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.forgerock.json.resource.UpdateRequest;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.Context;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.ChallengeResponse;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.ChallengeScheme;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.Conditions;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.MediaType;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.Method;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.Preference;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.Reference;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.data.Tag;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.ext.jackson.JacksonRepresentation;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.representation.EmptyRepresentation;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.representation.Representation;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.resource.ClientResource;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkimport org.restlet.resource.ResourceException;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk/**
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * A {@link Connection} to the remote OpenIDM instance.
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk */
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenkpublic class HttpRemoteJsonResource implements Connection {
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk /**
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * Requests that the origin server accepts the entity enclosed in the
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * request as a new subordinate of the resource identified by the request
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * URI.
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk *
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * @see <a
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5">HTTP
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * RFC - 9.5 POST</a>
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk */
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk public static final Method PATCH = new Method("PATCH");
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk /**Base reference used for requesting this resource. */
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk private Reference baseReference;
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk /** Username used for authentication when accessing the resource. */
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk private String username = "";
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk /** Password used for authentication when accessing the resource. */
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk private String password = "";
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk /**
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk * Construct the HttpRemoteJsonResource.
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk */
4b8d88eb610aa1e0bb6ec632f792744b3d6b5f22jeff.schenk public HttpRemoteJsonResource() {
}
/**
* Create a HttpRemoteJsonResource with credentials.
*
* @param uri URI of this resource.
* @param username Username for HTTP basic authentication
* @param password Password for HTTP basic authentication.
*/
public HttpRemoteJsonResource(final String uri, final String username, final String password) {
this.username = username;
this.password = password;
baseReference = new Reference(uri);
}
/**
* {@inheritDoc}
*/
@Override
public JsonValue action(org.forgerock.json.resource.Context context, ActionRequest request)
throws org.forgerock.json.resource.ResourceException {
JsonValue params = new JsonValue(request.getAdditionalParameters());
JsonValue result = handle(request, request.getResourceName(), params);
return result;
}
/**
* {@inheritDoc}
*/
@Override
public FutureResult<JsonValue> actionAsync(org.forgerock.json.resource.Context context,
ActionRequest request, ResultHandler<? super JsonValue> handler) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
}
/**
* {@inheritDoc}
*/
@Override
public Resource create(org.forgerock.json.resource.Context context, CreateRequest request)
throws org.forgerock.json.resource.ResourceException {
JsonValue result = handle(request, request.getResourceName() + "/" + request.getNewResourceId(), null);
return new Resource(result.get("_id").asString(), result.get("_rev").asString(), result);
}
/**
* {@inheritDoc}
*/
@Override
public FutureResult<Resource> createAsync(org.forgerock.json.resource.Context context,
CreateRequest request, ResultHandler<? super Resource> handler) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public Resource delete(org.forgerock.json.resource.Context context, DeleteRequest request)
throws org.forgerock.json.resource.ResourceException {
handle(request, request.getResourceName(), null);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public FutureResult<Resource> deleteAsync(org.forgerock.json.resource.Context context,
DeleteRequest request, ResultHandler<? super Resource> handler) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isClosed() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValid() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public Resource patch(org.forgerock.json.resource.Context context, PatchRequest request)
throws org.forgerock.json.resource.ResourceException {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public FutureResult<Resource> patchAsync(org.forgerock.json.resource.Context context,
PatchRequest request, ResultHandler<? super Resource> handler) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public QueryResult query(org.forgerock.json.resource.Context context, QueryRequest request,
QueryResultHandler handler) throws org.forgerock.json.resource.ResourceException {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public QueryResult query(org.forgerock.json.resource.Context context, QueryRequest request,
Collection<? super Resource> results)
throws org.forgerock.json.resource.ResourceException {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public FutureResult<QueryResult> queryAsync(org.forgerock.json.resource.Context context,
QueryRequest request, QueryResultHandler handler) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public Resource read(org.forgerock.json.resource.Context context, ReadRequest request)
throws org.forgerock.json.resource.ResourceException {
JsonValue result = handle(request, request.getResourceName(), null);
return new Resource(result.get("_id").asString(), result.get("_rev").asString(), result);
}
/**
* {@inheritDoc}
*/
@Override
public FutureResult<Resource> readAsync(org.forgerock.json.resource.Context context,
ReadRequest request, ResultHandler<? super Resource> handler) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public Resource update(org.forgerock.json.resource.Context context, UpdateRequest request)
throws org.forgerock.json.resource.ResourceException {
JsonValue result = handle(request, request.getResourceName(), null);
return new Resource(result.get("_id").asString(), result.get("_rev").asString(), result);
}
/**
* {@inheritDoc}
*/
@Override
public FutureResult<Resource> updateAsync(org.forgerock.json.resource.Context context,
UpdateRequest request, ResultHandler<? super Resource> handler) {
throw new UnsupportedOperationException();
}
private ClientResource getClientResource(Reference ref) {
ClientResource clientResource = new ClientResource(new Context(), new Reference(baseReference, ref));
List<Preference<MediaType>> acceptedMediaTypes = new ArrayList<Preference<MediaType>>(1);
acceptedMediaTypes.add(new Preference<MediaType>(MediaType.APPLICATION_JSON));
clientResource.getClientInfo().setAcceptedMediaTypes(acceptedMediaTypes);
clientResource.getLogger().setLevel(Level.WARNING);
ChallengeResponse rc = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, username, password);
clientResource.setChallengeResponse(rc);
return clientResource;
}
private JsonValue handle(Request request, String id, JsonValue params)
throws org.forgerock.json.resource.ResourceException {
Representation response = null;
ClientResource clientResource = null;
try {
Reference remoteRef = new Reference(id);
// Get the client resource corresponding to this request's resource name
clientResource = getClientResource(remoteRef);
// Prepare query params
if (params != null && !params.isNull()) {
for (Map.Entry<String, Object> entry : params.asMap().entrySet()) {
if (entry.getValue() instanceof String) {
clientResource.addQueryParameter(entry.getKey(), (String) entry.getValue());
}
}
}
// Payload
Representation representation = null;
JsonValue value = getRequestValue(request);
if (!value.isNull()) {
representation = new JacksonRepresentation<Map<String, Object>>(value.asMap());
}
// ETag
Conditions conditions = new Conditions();
switch (request.getRequestType()) {
case CREATE:
conditions.setNoneMatch(Arrays.asList(Tag.ALL));
clientResource.getRequest().setConditions(conditions);
response = clientResource.put(representation);
break;
case READ:
response = clientResource.get();
break;
case UPDATE:
conditions.setMatch(getTag(((UpdateRequest) request).getRevision()));
clientResource.getRequest().setConditions(conditions);
response = clientResource.put(representation);
break;
case DELETE:
conditions.setMatch(Arrays.asList(Tag.ALL));
clientResource.getRequest().setConditions(conditions);
response = clientResource.delete();
break;
case PATCH:
conditions.setMatch(getTag(((PatchRequest) request).getRevision()));
clientResource.getRequest().setConditions(conditions);
clientResource.setMethod(PATCH);
clientResource.getRequest().setEntity(representation);
response = clientResource.handle();
break;
case QUERY:
response = clientResource.get();
break;
case ACTION:
//clientResource.getRequest().setEntity(representation);
response = clientResource.post(representation);
break;
default:
throw new BadRequestException();
}
if (!clientResource.getStatus().isSuccess()) {
throw org.forgerock.json.resource.ResourceException.getException(clientResource
.getStatus().getCode(), clientResource.getStatus().getDescription(),
clientResource.getStatus().getThrowable());
}
JsonValue result = null;
if (null != response && !(response instanceof EmptyRepresentation)) {
result = new JsonValue(getJacksonObject(response));
} else {
result = new JsonValue(null);
}
return result;
} catch (JsonValueException jve) {
throw new BadRequestException(jve);
} catch (ResourceException e) {
StringBuilder sb = new StringBuilder(e.getStatus().getDescription());
if (null != clientResource) {
try {
sb.append(" ").append(clientResource.getResponse().getEntity().getText());
} catch (IOException e1) {
// unable to add response text to exception message, proceed without
}
}
throw org.forgerock.json.resource.ResourceException.getException(
e.getStatus().getCode(), sb.toString(), e.getCause());
} catch (Exception e) {
throw new InternalServerErrorException(e);
} finally {
if (null != response) {
response.release();
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private Map<String, Object> getJacksonObject(Representation response) throws IOException {
return (Map<String, Object>) new JacksonRepresentation<Map>(response, Map.class).getObject();
}
private JsonValue getRequestValue(Request request) throws Exception {
switch (request.getRequestType()) {
case CREATE:
return ((CreateRequest) request).getContent();
case UPDATE:
return new JsonValue(((UpdateRequest) request).getContent());
case PATCH:
ObjectMapper mapper = new ObjectMapper();
List<PatchOperation> ops = ((PatchRequest) request).getPatchOperations();
JsonValue value = new JsonValue(new ArrayList<Object>());
for (PatchOperation op : ops) {
value.add(new JsonValue(mapper.readValue(op.toString(), Object.class)));
}
return value;
case ACTION:
JsonValue content = ((ActionRequest) request).getContent();
if (content != null && !content.isNull()) {
return content;
} else {
return new JsonValue(new HashMap<String, Object>());
}
}
return new JsonValue(null);
}
private List<Tag> getTag(String tag) {
List<Tag> result = new ArrayList<Tag>(1);
if (null != tag && tag.trim().length() > 0) {
result.add(Tag.parse(tag));
}
return result;
}
/**
* Gets the username.
*
* @return the username
*/
public String getUsername() {
return username;
}
/**
* Sets the username.
*
* @param username the username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Gets the password.
*
* @return the password
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password the password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Sets the port.
*
* @param port the port
*/
public void setPort(int port) {
baseReference.setHostPort(port);
}
/**
* Sets the base URI.
*
* @param baseUri the base URI
*/
public void setBaseUri(final String baseUri) {
baseReference = new Reference(baseUri);
}
}