/*
* "Copyright (c) 2010-11 The Regents of the University of California.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
*
* Author: Jorge Ortiz (jortiz@cs.berkeley.edu)
* IS4 release version 1.0
*/
package local.rest.handlers;
import local.db.*;
import local.metadata.*;
import local.metadata.context.*;
import java.util.*;
import java.util.logging.Logger;
import java.util.logging.Level;
import net.sf.json.*;
import com.sun.net.httpserver.*;
import java.io.*;
public class MetadataHandler extends Filter implements HttpHandler {
private static Logger logger = Logger.getLogger(MetadataHandler.class.getPackage().getName());
//This addresses the HttpContext switch bug
//For every call to create a context in the httpServer after the root, the HttpContext object changes, and the filter
//is no longer used in the new object.
protected HttpContext thisContext = null;
protected static String URI = null;
private static final int META = 0;
private static final int CONTEXT = 1;
private static final int OBJECT = 2;
private static final int LOGIC = 3;
public MetadataHandler(String uri){
URI = uri;
}
public String description(){
return "MetadataHandler " + URI + " filter";
}
public void doFilter(HttpExchange exchange, Filter.Chain chain) throws IOException {
logger.info("doFilter");
boolean paramsOk = false;
if((paramsOk = parseParams(exchange)) && chain==null)
this.handle(exchange);
else if (!paramsOk)
sendResponse(exchange, 404, null);
else
chain.doFilter(exchange);
}
protected boolean parseParams(HttpExchange exchange) {
logger.info("parseParams");
logger.info("Request URI: " + exchange.getRequestURI().toString());
StringTokenizer tokenizer = new StringTokenizer(exchange.getRequestURI().toString(), "?");
if(tokenizer != null && tokenizer.hasMoreTokens()){
String thisResourcePath = tokenizer.nextToken();
if(URI == null && !thisResourcePath.equals(URI) && !thisResourcePath.equals(URI + "/"))
return false;
if(tokenizer.countTokens()>0) {
StringTokenizer paramStrTokenizer = new StringTokenizer(tokenizer.nextToken(), "&");
if(paramStrTokenizer !=null && paramStrTokenizer.hasMoreTokens()){
while (paramStrTokenizer.hasMoreTokens()){
StringTokenizer paramPairsTokenizer = new StringTokenizer(paramStrTokenizer.nextToken(),"=");
if(paramPairsTokenizer != null && paramPairsTokenizer.hasMoreTokens()){
String attr = paramPairsTokenizer.nextToken();
String val = paramPairsTokenizer.nextToken();
exchange.setAttribute(attr, val);
logger.info("Added (" + attr + ", " + val + ") pair to exchange session");
}
}
}
} else{
logger.fine("Not enough tokens");
}
}
return true;
}
private boolean filterCheck(HttpExchange exchange){
logger.info("filterCheck");
try {
//This addresses the HttpContext switch bug in the library
//The filter must be called BEFORE the handler
if (exchange.getHttpContext() != thisContext && exchange.getHttpContext().getFilters().size()==0) {
thisContext = exchange.getHttpContext();
thisContext.getFilters().add(this);
this.doFilter(exchange, null);
return true;
}
} catch (IOException e){
logger.log(Level.WARNING, "Could not carry out the Filter operation", e);
return false;
}
return false;
}
private int classifyResourceType(String uri){
StringTokenizer tokenizer = new StringTokenizer(uri, "?");
String u = tokenizer.nextToken();
if(u.endsWith("metadata")) return META;
else if(u.endsWith("metadata/context")) return CONTEXT;
else if (u.endsWith("metadata/object")) return OBJECT;
else if(u.endsWith("metadata/logic")) return LOGIC;
else return -1;
}
public void handle(HttpExchange exchange) throws IOException{
logger.info("handle");
//check if the filter was hit up
filterCheck(exchange);
classifyResourceType(exchange.getRequestURI().toString());
String requestMethod = exchange.getRequestMethod();
if (requestMethod.equalsIgnoreCase("GET")) {
handleGetRequest(exchange);
}
else if (requestMethod.equalsIgnoreCase("PUT")){
handlePutRequest(exchange);
}
else if (requestMethod.equalsIgnoreCase("POST") ){
handlePostRequest(exchange);
}
else if (requestMethod.equalsIgnoreCase("DELETE")){
handleDeleteRequest(exchange);
}
exchange.close();
}
private void handleGetRequest(HttpExchange exchange) {
logger.finer("GET Request received");
int resourceClass = classifyResourceType(exchange.getRequestURI().toString());
switch(resourceClass){
case META:
handleMetaGet(exchange);
break;
case CONTEXT:
handleContextGet(exchange);
break;
case LOGIC:
handleLogicGet(exchange);
break;
case OBJECT:
handleObjectGet(exchange);
break;
default:
JSONObject unknown = new JSONObject();
JSONArray errors = new JSONArray();
errors.add("Unknown resource");
unknown.put("errors", errors);
sendResponse(exchange, 200, unknown.toString());
break;
}
}
private void handleMetaGet(HttpExchange exchange){
String id = (String)exchange.getAttribute("pubid");
logger.fine("exchange.id=" + id);
if(id != null && !(new StringTokenizer(exchange.getRequestURI().toString(), "&").countTokens()>1)) {
processIdFetch(exchange, id);
} else {
JSONObject r = new JSONObject();
JSONArray errors = new JSONArray();
String errorMsg = "\"pubid\" URL parameter must be set";
errors.add(errorMsg);
logger.info(errorMsg);
r.put("operation", "get_metadata");
r.put("status", "fail");
r.put("errors", errors);
sendResponse(exchange, 200, r.toString());
}
exchange.setAttribute("pubid", null);
}
private boolean hasParams(HttpExchange exchange){
StringTokenizer t = new StringTokenizer(exchange.getRequestURI().toString(), "?");
if(t.countTokens()>1)
return true;
return false;
}
private void handleContextGet(HttpExchange exchange){
JSONObject response = new JSONObject();
JSONArray errors = new JSONArray();
logger.info("handleContextGet");
if(hasParams(exchange)){
String id = (String) exchange.getAttribute("id");
String getImage= (String) exchange.getAttribute("getimage");
//handle invalid requests
if(id==null) {
errors.add("Must specify a context id");
response.put("errors", errors.toString());
sendResponse(exchange, 200, response.toString());
} else if(id.length()<8){
errors.add("Invalid Id");
response.put("errors", errors.toString());
sendResponse(exchange, 200, response.toString());
}
//handle request base on id type
if(id.length()>=8){
JSONObject cmap = ((MySqlDriver)DBAbstractionLayer.database).getContextGraph(id.substring(0,8));
cmap.remove("name");
if(cmap != null && !cmap.toString().equals("{}")) {
//node or edge
if(id.length()>8 && id.charAt(8) == 'n'){
JSONArray nodes = cmap.getJSONArray("graph_nodes");
int i=0;
while(i<nodes.size()){
JSONObject currentNode = (JSONObject) nodes.get(i);
if(currentNode.getString("cnid").equals(id)){
sendResponse(exchange, 200, currentNode.toString());
return;
}
i+=1;
}
} else if(id.length()>8 && id.charAt(8) == 'e'){
JSONArray edges = cmap.getJSONArray("graph_edges");
int i=0;
while(i<edges.size()){
JSONObject currentEdge = (JSONObject) edges.get(i);
if(currentEdge.getString("ceid").equals(id)){
sendResponse(exchange, 200, currentEdge.toString());
return;
}
i+=1;
}
} else if(id.length()==8) {
if (getImage != null && getImage.equalsIgnoreCase("true")){
String dotOutput = dotConversion(cmap, "http://localhost/is4/metadata/context/dot");
String cid = cmap.getString("cid");
String iurl = "http://localhost/is4/metadata/context/dot/ContextMap_" + cid + ".svg";
sendDot(cid, dotOutput, "jortiz@jortiz81.homelinux.com", "/var/www/is4/metadata/context/dot");
cmap.put("imageUrl", iurl);
}
sendResponse(exchange, 200, cmap.toString());
}
} else {
errors.add("Could not find context information for id=" + id);
response.put("errors", errors);
sendResponse(exchange, 200, response.toString());
}
}
} else{
errors.add("Id must be provided");
response.put("errors", errors);
sendResponse(exchange, 200, response.toString());
}
}
private void handleLogicGet(HttpExchange exchange){
}
private void handleObjectGet(HttpExchange exchange){
}
private void handlePutRequest(HttpExchange exchange){
if(classifyResourceType(exchange.getRequestURI().toString()) == META){
String requestType = (String) exchange.getAttribute("type");
if(requestType != null && requestType.equalsIgnoreCase("context_graph")) {
JSONArray errors = new JSONArray();
//validate the input type
JSONObject metadata = getJSONRequestBody(exchange);
if(!ContextMngr.getInstance().addNewContextMap(metadata, errors)){
JSONObject response = new JSONObject();
response.put("errors", errors);
sendResponse(exchange, 200, response.toString());
}
}
} else { //put only on /is4/metadata
sendResponse(exchange, 400, null);
}
}
private void handlePostRequest(HttpExchange exchange){
}
private void handleDeleteRequest(HttpExchange exchange){
}
private void processIdFetch(HttpExchange exchange, String id) {
JSONObject r = new JSONObject();
if(id.equals("*")){
JSONObject all = new JSONObject();
JSONArray activeIds = MetadataMngr.getInstance().getAllBoundIds();
for(int i=0; i<activeIds.size(); i++){
String thisId = (String) activeIds.get(i);
JSONObject thisMeta = DBAbstractionLayer.database.getMetadata(thisId);
all.put(thisId, thisMeta);
}
sendResponse(exchange, 200, all.toString());
} else{
try {
if(r==null)
logger.warning("r is NULL");
if(id==null)
logger.warning("id is NULL");
//check valud UUID
UUID u = UUID.fromString(id);
JSONObject metadata = DBAbstractionLayer.database.getMetadata(id);
r.put("id", id);
r.put("metadata", metadata);
sendResponse(exchange, 200, r.toString());
}
catch(IllegalArgumentException e) {
JSONArray errors = new JSONArray();
String errorMsg = "Invalid UUID format for pubid: " + id;
errors.add(errorMsg);
logger.info(errorMsg);
r.put("operation", "get_metadata");
r.put("status", "fail");
r.put("errors", errors);
sendResponse(exchange, 200, r.toString());
}
}
}
private JSONObject getJSONRequestBody(HttpExchange exchange) {
try{
BufferedReader is = new BufferedReader(new InputStreamReader(exchange.getRequestBody()));
String line="";
StringBuffer bodyBuf = new StringBuffer();
while((line=is.readLine())!=null){
bodyBuf.append(line);
}
return (JSONObject) JSONSerializer.toJSON(bodyBuf.toString());
} catch (JSONException e){
logger.log(Level.WARNING, "Request Error: Invalid JSON format", e);
JSONObject r = new JSONObject();
JSONArray a = new JSONArray();
a.add("Request Error: Invalid JSON format");
r.put("operation", "add_metadata");
r.put("status", "fail");
r.put("errors", a);
sendResponse(exchange, 200, r.toString());
} catch (IOException ioe){
logger.log(Level.WARNING, "Could not get body", ioe);
}
return new JSONObject();
}
public String dotConversion(JSONObject cmap, String url){
try {
StringBuffer dotBuf = new StringBuffer().append("digraph ContextMap_").append(cmap.getString("cid")).append("{");
Hashtable<String, JSONObject> nodeLookupTable = new Hashtable<String, JSONObject>();
JSONArray nodes = cmap.getJSONArray("graph_nodes");
dotBuf.append("graph [URL=\"").
append("http://localhost:8080/is4/metadata/context?id=").append(cmap.getString("cid")).append("\"];");
for(int i=0; i<nodes.size(); i++){
JSONObject thisNode = (JSONObject)nodes.get(i);
String thisCnid = ((JSONObject)nodes.get(i)).getString("cnid");
nodeLookupTable.put(thisCnid, thisNode);
dotBuf.append("\"").append(thisNode.getString("label")).append("\" [URL=\"").
append("http://localhost:8080/is4/metadata/context?id=").append(thisNode.getString("cnid")).append("\"];");
}
JSONArray edges = cmap.getJSONArray("graph_edges");
for(int i=0; i<edges.size(); i++){
JSONObject thisEdge = (JSONObject) edges.get(i);
String sourceCnid = thisEdge.getString("sourceNode");
String destCnid = thisEdge.getString("destinationNode");
String sourceName = nodeLookupTable.get(sourceCnid).getString("label");
String destName = nodeLookupTable.get(destCnid).getString("label");
String dotEdge = "\"" + sourceName + "\"->\"" + destName + "\";";
dotBuf.append(dotEdge);
}
dotBuf.append("}");
return dotBuf.toString();
} catch(Exception e){
logger.log(Level.WARNING, "Error during conversion of graph to DOT", e);
return "";
}
}
public void sendDot(String cid, String dotOutput, String host, String uri){
String filename = "ContextMap_" + cid + ".dot";
String filename2 = "ContextMap_" + cid + ".svg";
try {
String makePngCommand = "dot -Tsvg " + filename + " -o ContextMap_" + cid + ".svg";
//String command = "scp " + filename2 + " " + host + ":" + uri;
String command = "cp " + filename2 + " " + "/var/www/is4/metadata/context/dot/";
String command2 = "cp " + filename + " " + "/var/www/is4/metadata/context/dot/";
System.out.println(command);
String deleteCmd1 = "rm -f " + filename2;
String deleteCmd2 = "rm -f " + filename;
File dotFile = new File(filename);
FileOutputStream dotFileOstream = new FileOutputStream(dotFile);
dotFileOstream.write(dotOutput.getBytes());
dotFileOstream.close();
System.out.println(makePngCommand);
Process p = Runtime.getRuntime().exec(makePngCommand);
System.out.println(command);
p = Runtime.getRuntime().exec(command);
p = Runtime.getRuntime().exec(command2);
p = Runtime.getRuntime().exec(deleteCmd1);
p = Runtime.getRuntime().exec(deleteCmd2);
}catch(Exception e){
logger.log(Level.WARNING, "Error sending dot file", e);
}
}
protected void sendResponse(HttpExchange exchange, int errorCode, String response){
try{
logger.info("Sending Response");
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("Content-Type", "application/json");
exchange.sendResponseHeaders(errorCode, 0);
OutputStream responseBody = exchange.getResponseBody();
if(response!=null)
responseBody.write(response.getBytes());
responseBody.close();
}catch(Exception e){
logger.log(Level.WARNING, "Exception thrown while sending response",e);
}
}
}