/*
* "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 2.0
*/
package local.rest;
import local.metadata.context.*;
import local.rest.handlers.*;
import local.db.*;
import local.rest.resources.*;
import local.rest.resources.util.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import java.util.logging.Level;
import is4.*;
import net.sf.json.*;
import com.sun.net.httpserver.*;
import javax.naming.InvalidNameException;
public class RESTServer {
private String bindAddress = "localhost";
private int port = 8080;
protected static Logger logger = Logger.getLogger(RESTServer.class.getPackage().getName());
private static HttpServer httpServer = null;
private static Hashtable<String, String> baseResources = new Hashtable<String, String>();
private static Hashtable<String, Resource> resourceTree = new Hashtable<String, Resource>();
public static final long start_time = (new Date().getTime())/1000;
private static final String rootPath = "/";
private static MongoDBDriver mdriver = new MongoDBDriver();
private static MetadataGraph metadataGraph = null;
public RESTServer(){}
public RESTServer(String address, int p){
logger = Logger.getLogger(RESTServer.class.getPackage().getName());
bindAddress = address;
port = p;
}
public static void main(String[] args){
RESTServer.logger = Logger.getLogger(RESTServer.class.getPackage().getName());
RESTServer restSvr = new RESTServer();
restSvr.start();
}
public void start(){
try {
System.setProperty("http.keepAlive", "false");
System.setProperty("http.maxConnections", "1");
System.setProperty("sun.net.http.errorstream.enableBuffering", "true");
logger.config("Starting RESTServer on HOST " + bindAddress + " PORT " + port);
//InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName(bindAddress), port);
//httpServer = HttpServer.create(addr, 0);
httpServer = HttpServer.create();
DBAbstractionLayer dbAbstractionLayer = new DBAbstractionLayer();
//Root handler
RootHandler handler = new RootHandler(rootPath);
RESTServer.addResource(handler);
baseResources.put(rootPath,"");
//Information bus resource
InfoBusResource ibus = InfoBusResource.getInstance(rootPath + "ibus/");
RESTServer.addResource(ibus);
baseResources.put(rootPath + "ibus/","");
//action handlers
StreamHandler streamHdlr = new StreamHandler();
SmapSourceHandler smapSourceHandler = new SmapSourceHandler(rootPath + "pub/smap/");
baseResources.put(rootPath + "pub/smap", "");
httpServer.createContext(rootPath + "streamtest", streamHdlr);
baseResources.put(rootPath + "streamtest/","");
//Resync smap
ResyncSmapStreams resyncResource = new ResyncSmapStreams(rootPath + "resync/");
baseResources.put(rootPath + "resync/", "");
RESTServer.addResource(resyncResource);
//Add filter for parsing URL parameters
String pubPath = rootPath + "pub/";
PubHandler pubHandler = new PubHandler(pubPath);
RESTServer.addResource(pubHandler);
baseResources.put(rootPath + "pub/","");
//Add filter for parsing URL parameters
HttpContext context2 = httpServer.createContext(rootPath + "pub/smap/", smapSourceHandler);
context2.getFilters().add(smapSourceHandler);
baseResources.put(rootPath + "pub/smap/", "");
//httpServer.createContext(rootPath + "sub", subHandler);
SubHandler subHandler = new SubHandler(rootPath +"sub/");
RESTServer.addResource(subHandler);
baseResources.put(rootPath + "sub/","");
//information handlers
StreamInfoHandler streamInfoHandler = new StreamInfoHandler(rootPath + "pub/all/");
RESTServer.addResource(streamInfoHandler);
//get the current time from this resource, for time-series queries
TimeResource timeResource = new TimeResource(rootPath + "time/");
RESTServer.addResource(timeResource);
baseResources.put(rootPath + "time/","");
//SubInfoHandler subInfoHandler = new SubInfoHandler();
SubInfoHandler subInfoHandler = new SubInfoHandler(rootPath + "sub/all/");
RESTServer.addResource(subInfoHandler);
baseResources.put(rootPath + "sub/all","");
httpServer.createContext(rootPath + "sub/mypublist",subInfoHandler);
baseResources.put(rootPath + "pub/all","");
baseResources.put(rootPath + "sub/mypublist", "");
//unpub and unsub
/*UnpubHandler unpubHandler = new UnpubHandler();
UnsubHandler unsubHandler = new UnsubHandler();
httpServer.createContext(rootPath + "unpub", unpubHandler);
httpServer.createContext(rootPath + "unsub", unsubHandler);
baseResources.put(rootPath + "unpub", "");
baseResources.put(rootPath + "unsub", "");
//sub control
SubControlHandler subCtrlHdlr = new SubControlHandler();
httpServer.createContext(rootPath + "sub/control", subCtrlHdlr);
baseResources.put(rootPath + "sub/control", "");*/
//Smap Message Demultiplexer for smap reports
DemuxResource demuxResource = new DemuxResource();
RESTServer.addResource(demuxResource);
//Model manager
ModelManagerResource mmr = new ModelManagerResource();
baseResources.put(mmr.getURI(),"");
RESTServer.addResource(mmr);
//setup admin resources
Resource adminResource = new Resource(rootPath + "admin/");
Resource tsResource = new Resource(adminResource.getURI() + "data/");
Resource propsResource = new Resource(adminResource.getURI() + "properties/");
Resource dataAdminResource = new AdminDataReposIndexesResource();
Resource propsAdminResource = new AdminPropsReposIndexesResource();
Resource allNodesResource = new AllNodesResource(rootPath + "admin/listrsrcs/");
baseResources.put(adminResource.getURI(), "");
baseResources.put(tsResource.getURI(), "");
baseResources.put(propsResource.getURI(), "");
baseResources.put(dataAdminResource.getURI(),"");
baseResources.put(propsAdminResource.getURI(),"");
baseResources.put(allNodesResource.getURI(),"");
RESTServer.addResource(adminResource);
RESTServer.addResource(tsResource);
RESTServer.addResource(propsResource);
RESTServer.addResource(dataAdminResource);
RESTServer.addResource(propsAdminResource);
RESTServer.addResource(allNodesResource);
//load saved resources
loadResources();
//load into in-memory metadata graph
metadataGraph = MetadataGraph.getInstance();
Resource.setMetadataGraph(metadataGraph);
httpServer.setExecutor(Executors.newCachedThreadPool());
//httpServer.setExecutor(Executors.newFixedThreadPool(1));
InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName(bindAddress), port);
httpServer.bind(addr,0);
httpServer.start();
System.out.println("Server is listening on port " + port );
}
catch (Exception e) {
logger.log(Level.WARNING, "", e);
//e.printStackTrace();
}
}
public static HttpServer getHttpServer(){
return httpServer;
}
public static void addResource(Resource resource){
if(resource != null && !baseResources.contains(resource.getURI()) &&
!resource.getURI().equals("") ){
logger.config("Adding contextHandler: \"" + resource.getURI() +
"\"\tLENGTH: " + resource.getURI().length());
HttpContext resourceContext = httpServer.createContext(resource.getURI(), resource);
resourceContext.getFilters().add(resource);
//add it to local resourceTree hashtable
resourceTree.put(resource.getURI(), resource);
logger.info("resourceTree.add: " + resource.getURI().toString());
//handle requests that end with and without "/"
String otherUrl = null;
if(resource.getURI().endsWith("/") && !resource.getURI().equals("/")){
otherUrl = resource.getURI().substring(0, resource.getURI().length()-1);
logger.config("Adding contextHandler: " + otherUrl);
resourceContext = httpServer.createContext(otherUrl, resource);
resourceContext.getFilters().add(resource);
//add it to local resourceTree hashtable
resourceTree.put(otherUrl, resource);
logger.info("resourceTree.add: " + otherUrl);
} else if(!resource.getURI().endsWith("/")){
otherUrl = resource.getURI() + "/";
logger.config("Adding contextHandler: " + otherUrl);
resourceContext = httpServer.createContext(otherUrl, resource);
resourceContext.getFilters().add(resource);
//add it to local resourceTree hashtable
resourceTree.put(otherUrl, resource);
logger.info("resourceTree.add: " + otherUrl);
}
}
}
public static void removeResource(Resource resource){
try {
if(resource != null && !baseResources.contains(resource.getURI()) ){
httpServer.removeContext(resource.getURI());
if(resource.getURI().endsWith("/")){
try{httpServer.removeContext(resource.getURI());}
catch(Exception e){}
try{httpServer.removeContext(resource.getURI().
substring(0, resource.getURI().length()-1));}
catch(Exception e){}
resourceTree.remove(resource.getURI());
resourceTree.remove(resource.getURI().
substring(0, resource.getURI().length()-1));
}else{
try{httpServer.removeContext(resource.getURI() + "/");}
catch(Exception e){}
try{httpServer.removeContext(resource.getURI());}
catch(Exception e){}
resourceTree.remove(resource.getURI());
resourceTree.remove(resource.getURI() + "/");
}
}
} catch(Exception e){
if(!(e instanceof java.lang.IllegalArgumentException))
logger.log(Level.WARNING, "", e);
}
}
public static boolean isBaseResource(String path){
return baseResources.contains(path);
}
public static Resource getResource(String path){
if(path == null)
return null;
logger.info("resourceTree.get: " + path);
return resourceTree.get(path);
}
public static boolean isResource(String path){
return resourceTree.containsKey(path);
}
public void loadResources(){
MySqlDriver database = (MySqlDriver)DBAbstractionLayer.database;
JSONArray allpaths = database.rrGetAllPaths();
logger.info("All_paths:\n" + allpaths.toString());
//logger.info("ALLPATHS: " + allpaths.toString());
UUID pubid=null;
try {
Vector<String> subsCreated = new Vector<String>();
logger.fine("Allpath_size: " + allpaths.size());
for(int i=0; i<allpaths.size(); i++){
logger.fine("getting path at position: " + i);
boolean alreadyAdded = false;
Resource resource = null;
String thisPath = (String)allpaths.get(i);
int rtype = ResourceUtils.translateType(database.getRRType(thisPath));
switch(rtype){
case ResourceUtils.DEFAULT_RSRC:
logger.info("Loading default resource: " + thisPath);
resource = new Resource(thisPath);
break;
case ResourceUtils.DEVICES_RSRC:
logger.info("Loading devices resource: " + thisPath);
resource = new DevicesResource(thisPath);
break;
case ResourceUtils.DEVICE_RSRC:
logger.info("Loading device resource: " + thisPath);
resource = new DeviceInstanceResource(thisPath);
break;
case ResourceUtils.PUBLISHER_RSRC:
logger.info("Loading publisher resource: " + thisPath);
pubid = database.isRRPublisher2(thisPath);
if(pubid!=null)
resource = new PublisherResource(thisPath, pubid);
else
resource = new Resource(thisPath);
break;
case ResourceUtils.GENERIC_PUBLISHER_RSRC:
logger.info("Loading publisher resource: " + thisPath);
pubid = database.isRRPublisher2(thisPath);
boolean modelstream = thisPath.startsWith("/models");
if(pubid!=null && !modelstream){
resource = new GenericPublisherResource(thisPath, pubid);
} else if(!modelstream){
resource = new Resource(thisPath);
}
break;
case ResourceUtils.SUBSCRIPTION_RSRC:
logger.info("Loading subscription resource: " + thisPath);
//get the subid associated with this uri
UUID subid = database.getSubId(thisPath);
if(subid != null)
resource = new SubscriptionResource(subid, thisPath);
else
resource = new Resource(thisPath);
break;
case ResourceUtils.SYMLINK_RSRC:
String link = database.getSymlinkAlias(thisPath);
logger.info("Loading symlink resource: " + thisPath + " links_to:" + link);
if(!link.equals("") && link.startsWith("http://")){
try {
URL thisUrl = new URL(link);
resource = new SymlinkResource(thisPath, thisUrl);
} catch(Exception e){
logger.log(Level.WARNING, "", e);
resource = new Resource(thisPath);
logger.info("Could not create symlink1");
}
} else if(!link.equals("") && link.startsWith("/")){
resource = new SymlinkResource(thisPath, link);
} else{
resource = new Resource(thisPath);
logger.info("Could not create symlink2");
}
break;
case ResourceUtils.MODEL_RSRC:
logger.info("Loading model resource: " + thisPath);
//resource may have been loaded by child, check if already registered
resource = RESTServer.getResource(thisPath);
boolean createView = false;
if(resource == null){
long last_model_ts = mdriver.getMaxTsModels(thisPath);
JSONObject mscript = mdriver.getModelEntry(thisPath, last_model_ts);
logger.info("mscript: " + mscript);
createView = (boolean)mscript.getBoolean("createview");
String rawscript = database.rrGetPropertiesStr(thisPath);
resource = new ModelResource(thisPath, rawscript, createView);
this.addResource(resource);
SubMngr submngr = SubMngr.getSubMngrInstance();
if(submngr.restartActiveModels((ModelResource)resource)){
logger.info("All active models for " + thisPath + " restarted and added successfully!");
} else {
logger.warning("Could not restart active models for: " + thisPath);
}
alreadyAdded=true;
}
break;
default:
resource = new Resource(thisPath);
break;
}
if(!alreadyAdded && resource != null){
logger.info("Adding: " + resource.getURI());
this.addResource(resource);
}
}
} catch(Exception e){
logger.log(Level.WARNING, "", e);
}
}
public class SubInfoHandler extends Resource{
public SubInfoHandler(String path) throws Exception, InvalidNameException{
super(path);
}
public void get(HttpExchange exchange, boolean internalCall, JSONObject internalResp){
JSONObject getSubsObj = new JSONObject();
getSubsObj.put("operation","get_all_subscribers");
getSubsObj.put("status", "success");
getSubsObj.put("subscribers",getSubsJSONArray());
sendResponse(exchange, 200, getSubsObj.toString(), internalCall, internalResp);
}
public void put(HttpExchange exchange, String data, boolean internalCall, JSONObject internalResp){
JSONObject infoReq = (JSONObject) JSONSerializer.toJSON(data);
if(infoReq.getString("name").equalsIgnoreCase("my_stream_list")){
String thisSubId = infoReq.getString("SubId");
SubMngr subMngr = SubMngr.getSubMngrInstance();
if(subMngr.isSubscriber(thisSubId)){
JSONArray publist = new JSONArray();
publist.addAll((Collection<String>) subMngr.getStreamIds(thisSubId));
JSONObject response = new JSONObject();
response.put("operation", "my_stream_list");
response.put("status","success");
response.put("PubList", publist);
sendResponse(exchange, 200, response.toString(), internalCall, internalResp);
}else{
JSONObject response = new JSONObject();
response.put("operation", "my_stream_list");
response.put("status","fail");
response.put("error", "Invalid subscriber id: " + thisSubId);
sendResponse(exchange, 200, response.toString(), internalCall, internalResp);
}
}else{
sendResponse(exchange, 401, null, internalCall, internalResp);
}
}
public void post(HttpExchange exchange, String data, boolean internalCall, JSONObject internalResp){
put(exchange, data, internalCall, internalResp);
}
public JSONArray getSubsJSONArray(){
JSONArray subIdsJSONObj = new JSONArray();
SubMngr subManager = SubMngr.getSubMngrInstance();
List<String> streamIds = subManager.getSubIds();
if(streamIds != null){
subIdsJSONObj.addAll(streamIds);
}
return subIdsJSONObj;
}
}
public class StreamInfoHandler extends Resource{
protected transient Logger logger = Logger.getLogger(StreamInfoHandler.class.getPackage().getName());
public StreamInfoHandler(String uri) throws Exception, InvalidNameException{
super(uri);
}
public void get(HttpExchange exchange, boolean internalCall, JSONObject internalResp){
try {
JSONObject getStreamsObj = new JSONObject();
getStreamsObj.put("operation","get_all_streams");
getStreamsObj.put("status", "success");
getStreamsObj.put("streams",getStreamIdsJSONArray());
sendResponse(exchange, 200, getStreamsObj.toString(), internalCall, internalResp);
} catch(Exception e){
logger.log(Level.WARNING, "", e);
}
}
public void put(HttpExchange exchange, String data, boolean internalCall, JSONObject internalResp){
sendResponse(exchange, 200, null, internalCall, internalResp);
}
public void post(HttpExchange exchange, String data, boolean internalCall, JSONObject internalResp){
put(exchange, data, internalCall, internalResp);
}
public void delete(HttpExchange exchange, boolean internalCall, JSONObject internalResp){
sendResponse(exchange, 200, null, internalCall, internalResp);
}
public JSONArray getStreamIdsJSONArray(){
JSONArray streamIdsJSONObj = new JSONArray();
Registrar pubManager = Registrar.registrarInstance();
ArrayList<String> streamIds = (ArrayList<String>) pubManager.getPubIds();
if(streamIds != null){
streamIdsJSONObj.addAll(streamIds);
}
return streamIdsJSONObj;
}
}
}