All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bagri.rest.RestRequestProcessor Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
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