com.bagri.rest.RestRequestProcessor Maven / Gradle / Ivy
package com.bagri.rest;
import static com.bagri.core.xquery.XQUtils.getAtomicValue;
import static com.bagri.rest.RestConstants.*;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import org.glassfish.jersey.process.Inflector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.bagri.core.api.ResultCursor;
import com.bagri.core.api.SchemaRepository;
import com.bagri.core.api.BagriException;
import com.bagri.core.system.Function;
import com.bagri.core.system.Parameter;
public class RestRequestProcessor implements Inflector {
private static final Logger logger = LoggerFactory.getLogger(RestRequestProcessor.class);
private static final String ns_http = "http://www.expath.org/http";
private static final String en_response = "response";
private static final String en_header = "header";
private static final String an_message = "message";
private static final String an_name = "name";
private static final String an_status = "status";
private static final String an_value = "value";
private Function fn;
private String query;
private RepositoryProvider rePro;
public RestRequestProcessor(Function fn, String query, RepositoryProvider rePro) {
this.fn = fn;
this.query = query;
this.rePro = rePro;
}
@Override
public Response apply(ContainerRequestContext context) {
String clientId = context.getCookies().get(bg_cookie).getValue();
SchemaRepository repo = rePro.getRepository(clientId);
logger.debug("apply.enter; path: {}; params: {}; query: {}", context.getUriInfo().getPath(),
context.getUriInfo().getPathParameters(), context.getUriInfo().getQueryParameters());
Map params = getParameters(context);
logger.debug("apply; got params: {}", params);
boolean empty = false;
Properties props = new Properties();
ResultCursor cursor = null;
try {
cursor = repo.getQueryManagement().executeQuery(query, params, props);
empty = !cursor.next();
} catch (BagriException ex) {
logger.error("apply.error: ", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
logger.debug("apply; got cursor: {}", cursor);
if (empty) {
// send response right away
return Response.ok().build();
}
Response.ResponseBuilder response = Response.noContent();
try {
empty = fillResponse(cursor, response);
} catch (BagriException ex) {
logger.error("apply.error: error processing response ", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
logger.debug("apply: got response: {}", response);
if (!empty) {
response.entity(getResultStream(cursor));
}
return response.build();
}
private Map getParameters(ContainerRequestContext context) {
Map params = new HashMap<>(fn.getParameters().size());
for (Parameter pm: fn.getParameters()) {
// TODO: resolve cardinality properly!
if (isPathParameter(pm.getName())) {
List vals = context.getUriInfo().getPathParameters().get(pm.getName());
if (vals != null) {
params.put(pm.getName(), getAtomicValue(pm.getType(), vals.get(0)));
}
} else {
String aType = getParamAnnotationType(pm.getName());
if (aType == null) {
// this is for POST/PUT only!
if (POST.equals(context.getMethod()) || PUT.equals(context.getMethod())) {
String body = getBody(context);
if (body != null) {
params.put(pm.getName(), getAtomicValue(pm.getType(), body));
}
}
} else {
boolean found = false;
List atns = Collections.emptyList();
switch (aType) {
case apn_cookie: {
Cookie val = context.getCookies().get(pm.getType());
if (val != null) {
params.put(pm.getName(), getAtomicValue(pm.getType(), val.getValue()));
found = true;
}
break;
}
case apn_form: {
// content type must be application/x-www-form-urlencoded
String body = getBody(context);
if (body != null) {
//logger.info("apply; form body: {}; ", body);
String val = getParamValue(body, "&", pm.getName());
if (val != null) {
params.put(pm.getName(), getAtomicValue(pm.getType(), val));
found = true;
}
}
break;
}
case apn_header: {
String val = context.getHeaderString(pm.getName()); //atns.get(0)); !!!
if (val != null) {
params.put(pm.getName(), getAtomicValue(pm.getType(), val));
found = true;
}
break;
}
case apn_matrix: {
// does not work in Jersey: context.getUriInfo().getPathSegments();
String val = getParamValue(context.getUriInfo().getPath(), "&", pm.getName());
if (val != null) {
params.put(pm.getName(), getAtomicValue(pm.getType(), val));
found = true;
}
break;
}
case apn_query: {
List vals = context.getUriInfo().getQueryParameters().get(pm.getName());
if (vals != null) {
params.put(pm.getName(), getAtomicValue(pm.getType(), vals.get(0)));
found = true;
}
break;
}
}
if (!found) {
setNotFoundParameter(params, atns, pm);
}
}
}
}
return params;
}
private boolean isPathParameter(String pName) {
List pa = fn.getAnnotations().get(an_path);
return (pa != null && pa.size() == 1 && pa.get(0).indexOf("{" + pName + "}") > 0);
}
private String getParamAnnotationType(String pName) {
String xpName = "{$" + pName + "}";
for (Map.Entry> ant: fn.getAnnotations().entrySet()) {
for (String val: ant.getValue()) {
if (pName.equals(val) || xpName.equals(val)) {
return ant.getKey();
}
}
}
return null;
}
private String getParamValue(String s, String d, String p) {
String[] parts = s.split(d);
for (String part: parts) {
int pos = part.indexOf("=");
if (pos > 0) {
String name = part.substring(0, pos);
if (name.equals(p)) {
return part.substring(pos + 1);
}
}
}
return null;
}
private String getBody(ContainerRequestContext context) {
if (context.hasEntity() && (POST.equals(context.getMethod()) || PUT.equals(context.getMethod()))) {
java.util.Scanner s = new java.util.Scanner(context.getEntityStream()).useDelimiter("\\A");
String result = s.next();
s.close();
return result;
}
return null;
}
private void setNotFoundParameter(Map params, List atns, Parameter pm) {
// handle default values
if (atns.size() > 2) {
params.put(pm.getName(), getAtomicValue(pm.getType(), atns.get(2)));
} else if (pm.getCardinality().isOptional()) {
// pass empty value..
params.put(pm.getName(), null);
}
}
private boolean fillResponse(ResultCursor cursor, Response.ResponseBuilder response) throws BagriException {
int status = Response.Status.OK.getStatusCode();
String message = null;
boolean empty = false;
Node node = null;
try {
node = cursor.getNode();
logger.debug("fillResponse; got node: {}", node);
} catch (BagriException ex) {
logger.debug("fillResponse; got non-xml content, skipping");
}
if (node != null) {
logger.trace("fillResponse; uri: {}; name: {}; type: {}", node.getNamespaceURI(), node.getNodeName(), node.getNodeType());
Element elt = (Element) node;
NodeList nodes = elt.getElementsByTagNameNS(ns_http, en_response);
if (nodes.getLength() > 0) {
elt = (Element) nodes.item(0);
String sts = elt.getAttribute(an_status);
if (!sts.isEmpty()) {
status = Integer.parseInt(sts);
}
message = elt.getAttribute(an_message);
// set Response headers..
NodeList children = elt.getElementsByTagNameNS(ns_http, en_header);
for (int i=0; i < children.getLength(); i++) {
elt = (Element) children.item(i);
String name = elt.getAttribute(an_name);
String value = elt.getAttribute(an_value);
if (!name.isEmpty() && !value.isEmpty()) {
response.header(name, value);
}
}
// move cursor one position further
empty = !cursor.next();
} else {
logger.debug("fillResponse; non-standard response structure: {}", elt);
}
}
response.status(status);
if (empty) {
response.entity(message);
}
return empty;
}
private StreamingOutput getResultStream(final ResultCursor result) {
return new StreamingOutput() {
@Override
public void write(OutputStream os) throws IOException, WebApplicationException {
try (Writer writer = new BufferedWriter(new OutputStreamWriter(os))) {
do {
String chunk = result.getItemAsString(null);
logger.trace("write; out: {}", chunk);
writer.write(chunk + "\n");
writer.flush();
} while (result.next());
} catch (BagriException ex) {
logger.error("write.error: error getting result from cursor ", ex);
// how to handle it properly?? throw WebAppEx?
} finally {
try {
result.close();
} catch (Exception ex) {
logger.error("write.error: error closing cursor ", ex);
}
}
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy