package com.mozilla.grouperfish.rest.jaxrs; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.nio.charset.Charset; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.StatusType; import javax.ws.rs.core.StreamingOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.mozilla.grouperfish.base.StreamTool; import com.mozilla.grouperfish.base.json.MapStreamer; import com.mozilla.grouperfish.model.Access; import com.mozilla.grouperfish.model.Access.Operation; import com.mozilla.grouperfish.naming.Scope; /** Bunch of internal helpers to cut down on resource specific code. */ class RestHelper { private static final Logger log = LoggerFactory.getLogger(RestHelper.class); /** Get any known resource. */ static Response getAny(final Class<?> resourceType, final Scope ns, final String key, final HttpServletRequest request) { if (!ns.allows(resourceType, new HttpAccess(Operation.READ, request))) return FORBIDDEN; final Map<String, String> map = ns.resourceMap(resourceType); final String source = map.get(key); if (source == null) { log.debug("404 Requested resource not found: {} {}", resourceType, ns.raw() + "::" + key); return NOT_FOUND; } return Response.ok(source, MediaType.APPLICATION_JSON).build(); } /** Put any known resource. */ static Response putAny(final Class<?> resourceType, final Scope ns, final String key, final HttpServletRequest request) throws IOException { Access access = new HttpAccess(Operation.CREATE, request); if (!ns.allows(resourceType, access)) return FORBIDDEN; final int maxLength = ns.maxLength(resourceType, access); if (maxLength < request.getContentLength()) return TOO_LARGE; final String data = StreamTool.maybeConsume(request.getInputStream(), Charset.forName("UTF-8"), maxLength); if (data == null) return RestHelper.TOO_LARGE; if (!ns.validator(resourceType).isValid(data)) return BAD_REQUEST; final Map<String, String> map = ns.resourceMap(resourceType); map.put(key, data); return Response.created(URI.create(key)).build(); } /** * You should only expose this API for small maps (queries, configuration). * @return a JSON map listing all named entities in this namespace and of this type. */ static Response listAny(final Class<?> resourceType, final Scope ns, final HttpServletRequest request) { if (!ns.allows(resourceType, new HttpAccess(Operation.LIST, request))) return FORBIDDEN; final MapStreamer streamer = new MapStreamer(ns.resourceMap(resourceType)); return Response.ok( new StreamingOutput() { @Override public void write(OutputStream output) throws IOException, WebApplicationException { streamer.write(output); } }, MediaType.APPLICATION_JSON ).build(); } /** Discard an item from the namespace. */ static Response deleteAny(final Class<?> resourceType, final Scope ns, final String key, final HttpServletRequest request) throws IOException { if (!ns.allows(resourceType, new HttpAccess(Operation.DELETE, request))) return FORBIDDEN; final Map<String, String> map = ns.resourceMap(resourceType); map.remove(key); return NO_CONTENT; } static final Response TOO_LARGE = Response.status( new StatusType() { public int getStatusCode() { return 413; } public Status.Family getFamily() { return Status.Family.CLIENT_ERROR; } public String getReasonPhrase() { return "Request Entity Too Large"; } }).build(); static final Response FORBIDDEN = Response.status(Status.FORBIDDEN).build(); static final Response BAD_REQUEST = Response.status(Status.BAD_REQUEST).build(); static final Response NOT_FOUND = Response.status(Status.NOT_FOUND).build(); static final Response ACCEPTED = Response.status(Status.ACCEPTED).build(); static final Response FAIL = Response.serverError().build(); static final Response NO_CONTENT = Response.noContent().build(); }