/* * "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.metadata.context; import local.rest.*; import local.db.*; import local.rest.resources.*; import net.sf.json.*; import jdsl.graph.api.*; import jdsl.graph.ref.*; import java.util.*; import java.util.logging.Logger; import java.util.logging.Level; import java.io.*; public class ContextMngr { private static transient Logger logger = Logger.getLogger(ContextMngr.class.getPackage().getName()); private static MySqlDriver database = (MySqlDriver) DBAbstractionLayer.database; private static ContextMngr contextMngr = null; //management modes private static final int NEW_GRAPH_MODE = 0; private static final int REPLACE_GRAPH_MODE = 1; private static final int REPLACE_NODE_MODE = 2; private static final int REPLACE_EDGE_MODE = 3; private static final int UPDATE_NODE_MODE = 4; private static final int UPDATE_EDGE_MODE = 5; private static final int REMOVE_GRAPH_MODE = 6; private static final int REMOVE_NODE_MODE = 7; private static final int REMOVE_EDGE_MODE = 8; private ContextMngr(){} public static ContextMngr getInstance(){ if(contextMngr == null) contextMngr = new ContextMngr(); return contextMngr; } public boolean addNewContextMap(JSONObject cmap, JSONArray errors){ try { int mode = NEW_GRAPH_MODE; if(cmap.getString("type").equalsIgnoreCase("context_graph")){ JSONArray jsonNodes = cmap.getJSONArray("graph_nodes"); JSONArray jsonEdges = cmap.optJSONArray("graph_edges"); if(!checkNames(jsonNodes, jsonEdges, errors)){ logger.warning("checkNames errors: " + errors.toString()); return false; } if(!checkInternalIds(jsonNodes, jsonEdges, errors)){ logger.warning("checkInternalIds errors: " + errors.toString()); return false; } addMissingEdges(jsonNodes, jsonEdges); addMissingFields(jsonNodes, jsonEdges); //save the graph createInternalGraph(jsonNodes, jsonEdges); String cid = jsonNodes.getJSONObject(0).getString("cnid").substring(0,8); cmap.put("cid", cid); cmap.put("graph_nodes", jsonNodes); cmap.put("graph_edges", jsonEdges); cmap.put("name", "context_graph"); DBAbstractionLayer.database.putEntry(cmap); //JSONObject resourceList = genResourceList(contextGraph); //generate resource tree String prefix = "/is4/Cory/lt/"; JSONObject treeResourceList = genTreeResourceList(cmap, errors); //logger.fine("PP_RESOURCE LIST:" + treeResourceList.toString()); if(treeResourceList != null){ Set<String> nodeNameSet = treeResourceList.keySet(); Iterator<String> nodeNameIter = nodeNameSet.iterator(); while(nodeNameIter.hasNext()){ String thisPath = prefix + treeResourceList.getString(nodeNameIter.next()); thisPath = thisPath.replace(" ", "_"); Resource resource =null; if(thisPath.endsWith("devices") || thisPath.endsWith("devices/")){ resource = (Resource) new DevicesResource(thisPath); } else if(thisPath.contains("devices")){ String subPath = thisPath.substring(thisPath.indexOf("devices"), thisPath.length()); StringTokenizer tokenizer = new StringTokenizer(subPath, "/"); if(tokenizer.countTokens()==2){ resource = (Resource) new DeviceInstanceResource(thisPath); } else if(tokenizer.countTokens()==3) { UUID pubid = database.isRRPublisher(thisPath); if(pubid != null) { resource = (Resource) new PublisherResource(thisPath, pubid); }else{ resource= new Resource(thisPath); } }else{ resource= new Resource(thisPath); } } else { resource = (Resource) new LoadTreeResource(thisPath, LoadTreeResource.PANEL_ELEMENT); } RESTServer.addResource(resource); } } else{ logger.fine("COULD NOT ADD LOAD TREE RESOURCES\n\tERRORS: " + errors.toString()); } } else { String e ="Can only add type: context_graph; Cannot add: " + cmap.getString("type"); errors.add(e); return false; } } catch(Exception e){ logger.log(Level.WARNING,"Error while add new context map", e); String msg = "Map may not adhere to context_map schema, check that all required fields are present"; errors.add(msg); return false; } return true; } public JSONObject genTreeResourceList(JSONObject contextGraph, JSONArray errors){ JSONArray nodes = contextGraph.getJSONArray("graph_nodes"); Hashtable<JSONObject, String> nodeToPath = new Hashtable<JSONObject, String>(); Hashtable<String, JSONObject> idToNode = new Hashtable<String, JSONObject>(); for(int i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject) nodes.get(i); idToNode.put(thisNode.getString("cnid"), thisNode); } Hashtable<String, String> labelToPath = new Hashtable<String, String>(); if(isTree(contextGraph, errors)){ for(int i=0; i<nodes.size(); i++){ Stack<String> pathElements = new Stack<String>(); JSONObject thisNode = (JSONObject) nodes.get(i); pathElements.push(thisNode.getString("label")); JSONArray parents = thisNode.getJSONArray("parents"); while(parents.size()>0){ thisNode = idToNode.get(parents.get(0)); pathElements.push(thisNode.getString("label")); parents = thisNode.getJSONArray("parents"); } StringBuffer thisPath = new StringBuffer(); while(!pathElements.empty()) thisPath.append(pathElements.pop()).append("/"); labelToPath.put(((JSONObject)nodes.get(i)).getString("name"), thisPath.toString()); } JSONObject allpaths = new JSONObject(); allpaths.accumulateAll(labelToPath); return allpaths; } else { logger.fine("NOT A TREE!"); return null; } } public boolean isTree(JSONObject contextGraph, JSONArray errors){ //boolean istree = true; JSONArray nodes = contextGraph.getJSONArray("graph_nodes"); int rootCount= 0; Vector<String> rootNames = new Vector<String>(); for(int i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject) nodes.get(i); JSONArray children = thisNode.getJSONArray("children"); JSONArray parents = thisNode.getJSONArray("parents"); if(parents.size() > 1){ errors.add("node_" + thisNode.getString("cnid") + " has more than one parent"); return false; } if(parents.size() == 0) { rootNames.addElement(thisNode.getString("name")); rootCount+=1; } if(rootCount > 1){ errors.add("Multiple root nodes"); errors.add(rootNames.elementAt(0)); errors.add(rootNames.elementAt(1)); return false; } for(int j=0; j<children.size(); j++){ if(parents.contains(children.get(j))){ errors.add("node_" + thisNode.getString("cnid") + " share the same parent and child: " + (String)children.get(j)); return false; } } } return true; } private boolean checkNodeSchema(int mode, JSONObject node){ return true; } private boolean checkEdgeSchema(int mode, JSONObject edge){ return true; } private void addMissingFields(JSONArray nodes, JSONArray edges) { int i; for(i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject) nodes.get(i); if(!thisNode.containsKey("label")) thisNode.put("label", thisNode.getString("name")); if(!thisNode.containsKey("AssociatedDevices")) thisNode.put("AssociatedDevices", new JSONArray()); if(!thisNode.containsKey("description")) thisNode.put("desciption", ""); if(!thisNode.containsKey("location")) thisNode.put("location", ""); if(!thisNode.containsKey("$schema")) thisNode.put("$schema", "http://jortiz81.homelinux.com/is4/schemas/context_node_schema.json"); } for(i=0; i<edges.size(); i++){ JSONObject thisEdge = (JSONObject) edges.get(i); if(!thisEdge.containsKey("label")) thisEdge.put("label", thisEdge.getString("name")); } } private boolean checkInternalIds(JSONArray nodes, JSONArray edges, JSONArray errors) { String stage = ""; try { int i; //load all internal ids and the node it references Hashtable<Integer, JSONObject> internalNodeIds = new Hashtable<Integer, JSONObject>(); for(i=0; i<nodes.size(); ++i){ JSONObject thisNode = (JSONObject) nodes.get(i); stage = "Checking cnid for node " + i + " in graph_nodes array; must be an integer"; internalNodeIds.put((new Integer(thisNode.getInt("cnid"))), thisNode); stage=""; } //check all the references and fix them if needed for(i=0; i<nodes.size(); ++i){ JSONObject thisNode = (JSONObject) nodes.get(i); stage = "Checking parent ids for node " + i + " in the array"; JSONArray parents = thisNode.getJSONArray("parents"); JSONArray filteredParents = new JSONArray(); int j; for(j=0; j<parents.size(); j++){ Integer thisParentId = new Integer(parents.getInt(j)); if(internalNodeIds.containsKey(thisParentId)) filteredParents.add(thisParentId); } thisNode.put("parents", filteredParents); stage = "Checking child ids for node " + i + " in the array"; JSONArray children = thisNode.getJSONArray("children"); JSONArray filteredChildren = new JSONArray(); for(j=0; j<children.size(); j++){ Integer thisChildId = new Integer(children.getInt(j)); if(internalNodeIds.containsKey(thisChildId)) filteredChildren.add(thisChildId); } thisNode.put("children", filteredChildren); stage = ""; //replace the internal node references internalNodeIds.put(new Integer(thisNode.getInt("cnid")), thisNode); //replace the nodes list to point to a cleaned up, valid, consistent node list Iterator<JSONObject> nodeJSONIterator = internalNodeIds.values().iterator(); JSONArray validNodes = new JSONArray(); while(nodeJSONIterator.hasNext()) validNodes.add(nodeJSONIterator.next()); //replace the elements in the nodes array if necessary, leaving only valid nodes if(nodes.size() != validNodes.size()){ while(nodes.size()>0) nodes.remove(0); for(int k=0; k<validNodes.size(); k++) nodes.add(validNodes.get(i)); } } //check all edge references and fix them if needed JSONArray validEdges = new JSONArray(); for(i=0; i<edges.size(); ++i){ JSONObject thisEdge = (JSONObject) edges.get(i); stage = "checking edge " + i; Integer sourceId = new Integer(thisEdge.getInt("sourceNode")); Integer destinationId = new Integer(thisEdge.getInt("destinationNode")); if(internalNodeIds.containsKey(sourceId) && internalNodeIds.containsKey(destinationId)) validEdges.add(thisEdge); else logger.info("Could not fined one of the node for edge " + thisEdge.getString("name")); stage = ""; } //replace the elements in the edges array, leaving only valid edges if(edges.size() != validEdges.size()){ while(edges.size()>0) edges.remove(0); for(i=0; i<validEdges.size(); i++) edges.add(validEdges.get(i)); } } catch (JSONException e){ logger.log(Level.WARNING, "JSONException caught", e); errors.add(stage); return false; } return true; } private boolean checkNames(JSONArray nodes, JSONArray edges, JSONArray errors){ try { Hashtable<String, JSONObject> internalNodeNames = new Hashtable<String, JSONObject>(); int i; for(i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject) nodes.get(i); if(!internalNodeNames.containsKey(thisNode.getString("name"))) { internalNodeNames.put(thisNode.getString("name"), thisNode); } else { errors.add("Duplicate node name " + thisNode.getString("name") + "; all node name must be unique"); return false; } } Hashtable<String, JSONObject> internalEdgeNames = new Hashtable<String, JSONObject>(); for(i=0; i<edges.size(); ++i){ JSONObject thisEdge = (JSONObject) edges.get(i); if(!internalEdgeNames.containsKey(thisEdge.getString("name"))) { internalEdgeNames.put(thisEdge.getString("name"), thisEdge); } else { errors.add("Duplicate edge name " + thisEdge.getString("name") + "; all node name must be unique"); return false; } } } catch(JSONException e){ logger.log(Level.WARNING, "", e); return false; } return true; } private void addMissingEdges(JSONArray nodes, JSONArray edges){ int edgeCounter = 0; String edgeNamePrefix = "edge_"; int i; //add missing edge objects Hashtable<String, JSONObject> edgeNames = new Hashtable<String, JSONObject>(); for(i=0; i<edges.size(); i++){ JSONObject thisEdge = (JSONObject) edges.get(i); edgeNames.put(thisEdge.getString("name"), thisEdge); } //Gather all edges introduced by node references Hashtable<String, JSONObject> nodeEdges = new Hashtable<String, JSONObject>(); for(i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject) nodes.get(i); JSONArray parents = thisNode.getJSONArray("parents"); int j; for(j=0; j<parents.size(); j++){ Integer sourceId = new Integer(parents.getInt(j)); Integer destId = new Integer(thisNode.getInt("cnid")); String newNodeEdgeStr = sourceId.toString() + "->" + destId.toString(); if(!nodeEdges.containsKey(newNodeEdgeStr)){ //new edge JSONObject newEdge = new JSONObject(); String newEdgeName = edgeNamePrefix + edgeCounter; while(edgeNames.containsKey(newEdgeName)){ edgeCounter += 1; newEdgeName = edgeNamePrefix + edgeCounter; } newEdge.put("label", newEdgeName); newEdge.put("name", newEdgeName); newEdge.put("type", "context_edge"); newEdge.put("sourceNode", sourceId); newEdge.put("destinationNode", destId); edgeNames.put(newEdgeName, newEdge); edges.add(newEdge); nodeEdges.put(newNodeEdgeStr, newEdge); } } JSONArray children = thisNode.getJSONArray("children"); for(j=0; j<children.size(); j++){ Integer sourceId = new Integer(thisNode.getInt("cnid")); Integer destId = new Integer(children.getInt(j)); String newNodeEdgeStr = sourceId + "->" + destId; if(!nodeEdges.containsKey(newNodeEdgeStr)){ //new edge JSONObject newEdge = new JSONObject(); String newEdgeName = edgeNamePrefix + edgeCounter; while(edgeNames.containsKey(newEdgeName)){ edgeCounter += 1; newEdgeName = edgeNamePrefix + edgeCounter; } newEdge.put("label", newEdgeName); newEdge.put("name", newEdgeName); newEdge.put("type", "context_edge"); newEdge.put("sourceNode", sourceId); newEdge.put("destinationNode", destId); edgeNames.put(newEdgeName, newEdge); edges.add(newEdge); nodeEdges.put(newNodeEdgeStr, newEdge); } } } //populate a lookupTable from internalId to jsonNode and dump all parent/children references Hashtable<Integer, JSONObject> internalIdLookupTable = new Hashtable<Integer, JSONObject>(); for(i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject)nodes.get(i); internalIdLookupTable.put(new Integer(thisNode.getInt("cnid")), thisNode); //dump all parent/children references JSONArray parents = thisNode.getJSONArray("parents"); JSONArray children = thisNode.getJSONArray("children"); while(parents.size()>0) parents.remove(0); while(children.size()>0) children.remove(0); } //re-populate all children/parent references for(i=0; i<edges.size(); i++){ JSONObject thisEdge = (JSONObject) edges.get(i); Integer sourceId = new Integer(thisEdge.getInt("sourceNode")); Integer destId = new Integer(thisEdge.getInt("destinationNode")); JSONObject sourceNode = internalIdLookupTable.get(sourceId); JSONObject destNode = internalIdLookupTable.get(destId); JSONArray sChildren = sourceNode.getJSONArray("children"); JSONArray dParents = destNode.getJSONArray("parents"); sChildren.add(destId); dParents.add(sourceId); } } 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 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(); Process p = Runtime.getRuntime().exec(makePngCommand); p = Runtime.getRuntime().exec(command); p = Runtime.getRuntime().exec(deleteCmd1); p = Runtime.getRuntime().exec(deleteCmd2); }catch(Exception e){ e.printStackTrace(); } } private IncidenceListGraph createInternalGraph(JSONArray nodes, JSONArray edges){ String cid = fixReferences(nodes, edges); Hashtable<String, ContextGraphNode> nodeNameLookupTable = new Hashtable<String, ContextGraphNode>(); IncidenceListGraph graph = new IncidenceListGraph(); int i; try{ for(i=0; i<nodes.size(); i++){ ContextGraphNode cgraphNode = new ContextGraphNode((JSONObject) nodes.get(i)); //Vertex v = graph.insertVertex(cgraphNode); nodeNameLookupTable.put(((JSONObject)nodes.get(i)).getString("cnid"), cgraphNode); } for(i=0; i<edges.size(); i++){ ContextGraphEdge cgraphEdge = new ContextGraphEdge((JSONObject) edges.get(i)); ContextGraphNode src = nodeNameLookupTable.get(((JSONObject)edges.get(i)).getString("sourceNode")); ContextGraphNode dst = nodeNameLookupTable.get(((JSONObject)edges.get(i)).getString("destinationNode")); //Edge e = graph.insertEdge(src, dst, cgraphEdge); } } catch(Exception e){ logger.log(Level.WARNING, "",e); } return graph; } private String fixReferences(JSONArray nodes, JSONArray edges){ //generate context graph prefix String contextGraphId = UUID.randomUUID().toString().substring(0,8); //fix nodes Hashtable<Integer, JSONObject> internalRefs = new Hashtable<Integer, JSONObject>(); Hashtable<Integer, String> internalToGlobalMap = new Hashtable<Integer, String>(); int i; for(i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject) nodes.get(i); Integer internalId = new Integer(thisNode.getInt("cnid")); internalRefs.put(internalId, thisNode); String newCnid = contextGraphId + "n-" + thisNode.getString("name"); thisNode.put("cnid", newCnid); internalToGlobalMap.put(internalId, newCnid); } for(i=0; i<nodes.size(); i++){ JSONObject thisNode = (JSONObject) nodes.get(i); JSONArray newParentIds = new JSONArray(); JSONArray oldParentIds = thisNode.getJSONArray("parents"); int j; for(j=0; j<oldParentIds.size(); j++){ Integer internalId = new Integer(oldParentIds.getInt(j)); if(internalToGlobalMap.containsKey(internalId)) newParentIds.add(internalToGlobalMap.get(internalId)); } thisNode.put("parents", newParentIds); JSONArray newChildrenIds = new JSONArray(); JSONArray oldChildrenIds = thisNode.getJSONArray("children"); for(j=0; j<oldChildrenIds.size(); j++){ Integer internalId = new Integer(oldChildrenIds.getInt(j)); if(internalToGlobalMap.containsKey(internalId)) newChildrenIds.add(internalToGlobalMap.get(internalId)); } thisNode.put("children", newChildrenIds); } //fix edges for(i=0; i<edges.size(); i++){ JSONObject thisEdge = (JSONObject) edges.get(i); String newSourceId = ""; Integer internalSourceId = new Integer(thisEdge.getInt("sourceNode")); if(internalToGlobalMap.containsKey(internalSourceId)) newSourceId = internalToGlobalMap.get(internalSourceId); thisEdge.put("sourceNode", newSourceId); String newDestId = ""; Integer internalDestId = new Integer(thisEdge.getInt("destinationNode")); if(internalToGlobalMap.containsKey(internalDestId)) newDestId = internalToGlobalMap.get(internalDestId); thisEdge.put("destinationNode", newDestId); String ceid = contextGraphId + "e-" + thisEdge.getString("name"); thisEdge.put("ceid", ceid); } return contextGraphId; } public boolean deleteContextMap(String cid, JSONArray errors){ return true; } public boolean updateContextMap(String cid, JSONObject cnode, JSONArray errors){ return true; } public boolean addNodeToContextMap (String cid, JSONObject cnode, JSONArray errors){ return true; } public boolean removeNodeFromContextMap(String cid, String nodeId){ return true; } public boolean replaceContextMap(String cid, JSONObject cmap){ return true; } public String dotConversion(JSONObject cmap){ try { StringBuffer dotBuf = new StringBuffer().append("digraph ").append(cmap.getString("cid")).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 dotEdge = sourceCnid + "->" + destCnid + ";"; dotBuf.append(dotEdge); } dotBuf.append("}"); return dotBuf.toString(); } catch(Exception e){ logger.log(Level.WARNING, "Error during conversion of graph to DOT", e); return ""; } } }