/* * "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 is4; import java.util.logging.Logger; import java.util.logging.Level; import java.util.*; import java.io.*; import java.net.*; import java.util.concurrent.Semaphore; import com.sun.net.httpserver.*; import local.db.*; import local.rest.*; import local.rest.resources.*; import local.rest.resources.util.*; import java.util.concurrent.ConcurrentHashMap; import java.nio.ByteBuffer; import java.nio.channels.Pipe; import java.nio.channels.Pipe.SinkChannel; import java.nio.channels.Pipe.SourceChannel; import net.sf.json.*; import is4.exceptions.*; /** * Subscription manager. Maintains subscriber and flow information. */ public class SubMngr { private static SubMngr subManager = null; protected static transient final Logger logger = Logger.getLogger(SubMngr.class.getPackage().getName()); protected static MySqlDriver database = (MySqlDriver) DBAbstractionLayer.database; //subscription identifer associated with a pipe that writes to the model thread //private static ConcurrentHashMap<UUID, Pipe> activePipes = new ConcurrentHashMap<UUID, Pipe>(); //subscription identifier associated with a semaphore that used by the thread that this subscription writes to //private static ConcurrentHashMap<UUID, Semaphore> readSemaphores = new ConcurrentHashMap<UUID, Semaphore>(); //same as above, but references by publisher identifer --this is the right way to do it since the //subscription source may be a wildcard private static ConcurrentHashMap<UUID, Pipe> activePipesByPubId = new ConcurrentHashMap<UUID, Pipe>(); private static ConcurrentHashMap<UUID, Semaphore> readSemaphoresByPubId = new ConcurrentHashMap<UUID, Semaphore>(); private SubMngr(){ } /** * Constructor. */ public static SubMngr getSubMngrInstance(){ if(subManager==null) subManager = new SubMngr(); return subManager; } /** * Return the list of subscription IDs. */ public List<String> getSubIds(){ return (List<String>) database.getAllSubIds(); } /** * Return the streams subscribed to by the process with the given subscription ID. */ public List<String> getStreamIds(String subId){ try { UUID sid = UUID.fromString(subId); return (List<String>) database.getAssocStreamUris(sid); } catch (Exception e){ return (List<String>)new JSONArray(); } } /** * The associated subscriber url for this subscriber, null if it doesn't have one or the subcriber does not exist. */ public String getSubUrl(String subId){ try { UUID sid = UUID.fromString(subId); return database.getSubDestUrlStr(sid); } catch(Exception e){ return null; } } /** * Initializes a new subscription using the specified wild-card path to the target (path or url). * Any publisher that matches the wildcard expression will push data to the specified target. The wildcard * must match at least one publisher in order to be installed. Wildcard subscription MUST be explicitly * deleted, unlike single-publisher subscriptions. * * @param wildcardPath a path with the * wildcard character in it. <br> * example:<br> * /buildings/SodaHall/electrical/*<br> * All publishers witht eh /buildings/SodaHall/eletrical prefix in their path will * forward their data to the specified target.<br><br> * @param target either a path to a model or processing resource within StreamFS, or a URL. The * URL must begin with "http://". * @return subscription creation message or a message with errors */ public synchronized JSONObject initSubscription(String wildcardPath, String target){ JSONObject response = new JSONObject(); JSONArray errors = new JSONArray(); try { String newSubId = null; logger.info("Trying to create subscription from " + wildcardPath + " to " + target); String usid = database.getSubscriptionId(null, wildcardPath, target); boolean pubMatch = database.wildcardPubMatch(wildcardPath); if( pubMatch && usid==null) { newSubId = generateNewId(); response.put("subid", newSubId); if(target.startsWith("http://")){ //create initial entry database.insertNewSubEntry(UUID.fromString(newSubId), null, null, target, null, null, wildcardPath); return response; } } else { String e=null; if(!pubMatch){ e = wildcardPath + " does not match any valid publisher"; logger.warning(e); errors.add(e); } if(usid !=null){ e = "The subscriber with (wildcard, target):(" + wildcardPath + ", " + target + ") is already a subscriber; Could not add as new subscriber"; logger.warning(e); errors.add(e); } response.put("errors", errors); return response; } if(target.startsWith("/")){ //the target uri/path; try to create a model thread and pipe Resource r = RESTServer.getResource(target); if(r != null && (r.TYPE==ResourceUtils.MODEL_RSRC || r.TYPE==ResourceUtils.MODEL_GENERIC_PUBLISHER_RSRC)){ if(r.TYPE==ResourceUtils.MODEL_RSRC){ Pipe newPipe = Pipe.open(); Semaphore s = new Semaphore(1, true); ModelResource modelres = (ModelResource)r; Thread modelresThreadInstance = ModelResource.startNewModelThread(s, newPipe.source(),modelres); /*UUID unewid = UUID.fromString(newSubId); if(activePipes.containsKey(unewid)){ activePipes.replace( unewid, newPipe); readSemaphores.put(unewid, s); } else { activePipes.put(unewid, newPipe); readSemaphores.put(unewid, s); }*/ //populate pipes references by publisher id/thread name UUID modelPubId = UUID.fromString(modelresThreadInstance.getName()); if(activePipesByPubId.containsKey(modelPubId)){ activePipesByPubId.replace(modelPubId, newPipe); } else { activePipesByPubId.put(modelPubId, newPipe); } if(readSemaphoresByPubId.containsKey(modelPubId)){ readSemaphoresByPubId.replace(modelPubId, s); } else { readSemaphoresByPubId.put(modelPubId, s); } String newTargetPubUri = database.getIs4RRPath(UUID.fromString(modelresThreadInstance.getName())); logger.info("Registered new pipe for [" + wildcardPath + ", " + newTargetPubUri + "]=(subid="+ newSubId + ")"); //create initial entry database.insertNewSubEntry(UUID.fromString(newSubId), null, null, null, newTargetPubUri, null, wildcardPath); } else if(r.TYPE==ResourceUtils.MODEL_GENERIC_PUBLISHER_RSRC){ //this is a subscription where the model_instance is the target //there is a pipe associated with this target, just add a subscription entry //create initial entry database.insertNewSubEntry(UUID.fromString(newSubId), null, null, null, r.getURI(), null, wildcardPath); } } else { errors.add("Target path must be a MODEL or PROCESSING resource"); response = response.discard("subid"); response.put("errors", errors); } } else { errors.add("Target path must ABSOLUTE (start with \"/\")"); response = response.discard("subid"); response.put("errors", errors); } return response; } catch (Exception m){ logger.log(Level.WARNING, "Exception caught in addSub(String url_, List<String> streams)", m); } response.put("errors",errors); return response; } /** * Initialize a new subscription from the publisher (pubid) to the target (path or url). */ public synchronized JSONObject initSubscription(UUID pubid, String target){ JSONObject response = new JSONObject(); JSONArray errors = new JSONArray(); try { String newId = null; logger.info("Trying to create subscription from " + pubid.toString() + " to " + target); String usid = database.getSubscriptionId(pubid, null, target); boolean isValidPub = database.isPublisher(pubid); if( isValidPub && usid==null) { newId = generateNewId(); response.put("subid", newId); if(target.startsWith("http://")){ //create initial entry database.insertNewSubEntry(UUID.fromString(newId), null, null, target, null, pubid, null); return response; } } else { String e=null; if(!isValidPub){ e = pubid.toString() + " is not a valid publisher"; logger.warning(e); errors.add(e); } if(usid !=null){ String pubPath = database.getIs4RRPath(pubid); e = "The subscriber with (source, target):(" + pubPath + ", " + target + ") is already a subscriber; Could not add as new subscriber"; logger.warning(e); errors.add(e); } response.put("errors", errors); return response; } if(target.startsWith("/")){ //the target uri/path; try to create a model thread and pipe Resource r = RESTServer.getResource(target); if(r != null && (r.TYPE==ResourceUtils.MODEL_RSRC || r.TYPE==ResourceUtils.MODEL_GENERIC_PUBLISHER_RSRC)){ if(r.TYPE==ResourceUtils.MODEL_RSRC){ Pipe newPipe = Pipe.open(); Semaphore s = new Semaphore(1, true); ModelResource modelres = (ModelResource)r; Thread modelresThreadInstance = ModelResource.startNewModelThread(s, newPipe.source(),modelres); /*UUID unewid = UUID.fromString(newId); if(activePipes.containsKey(unewid)){ activePipes.replace( unewid, newPipe); readSemaphores.put(unewid, s); } else { activePipes.put( unewid, newPipe); readSemaphores.put(unewid, s); }*/ //populate pipes references by publisher id/thread name UUID modelPubId = UUID.fromString(modelresThreadInstance.getName()); if(activePipesByPubId.containsKey(modelPubId)){ activePipesByPubId.replace(modelPubId, newPipe); } else { activePipesByPubId.put(modelPubId, newPipe); } if(readSemaphoresByPubId.containsKey(modelPubId)){ readSemaphoresByPubId.replace(modelPubId, s); } else { readSemaphoresByPubId.put(modelPubId, s); } String newTargetPubUri = database.getIs4RRPath(modelPubId); logger.info("Registered new pipe for [" + pubid.toString() + ", " + newTargetPubUri + "]=(subid="+ newId + ")"); //create initial entry database.insertNewSubEntry(UUID.fromString(newId), null, null, null, newTargetPubUri, pubid, null); } else if(r.TYPE==ResourceUtils.MODEL_GENERIC_PUBLISHER_RSRC){ //this is a subscription where the model_instance is the target //there is a pipe associated with this target, just add a subscription entry //create initial entry database.insertNewSubEntry(UUID.fromString(newId), null, null, null, r.getURI(), pubid, null); } } else { errors.add("Target path must be a MODEL resource"); response = response.discard("subid"); response.put("errors", errors); } } else { errors.add("Target path must ABSOLUTE (start with \"/\")"); response = response.discard("subid"); response.put("errors", errors); } return response; } catch (Exception m){ logger.log(Level.WARNING, "Exception caught in addSub(String url_, List<String> streams)", m); } response.put("errors",errors); return response; } public boolean restartActiveModels(ModelResource modelres){ boolean allok = true; if(modelres != null){ JSONArray modelPubIdsArray = database.getModelPubliserIds(modelres.getURI()); for(int i=0; i<modelPubIdsArray.size(); i++){ try { Pipe newPipe = Pipe.open(); Semaphore s = new Semaphore(1, true); UUID pubid = UUID.fromString((String)modelPubIdsArray.get(i)); Thread modelresThreadInstance = ModelResource.restartModelThread(s, newPipe.source(),modelres, pubid); if(modelresThreadInstance != null){ //populate pipes references by publisher id/thread name if(activePipesByPubId.containsKey(pubid)){ activePipesByPubId.replace(pubid, newPipe); } else { activePipesByPubId.put(pubid, newPipe); } if(readSemaphoresByPubId.containsKey(pubid)){ readSemaphoresByPubId.replace(pubid, s); } else { readSemaphoresByPubId.put(pubid, s); } logger.info("Thread-" + modelresThreadInstance.getName() + " restarted!"); } else { allok=false; } } catch(Exception e){ logger.log(Level.WARNING, "", e); allok=false; } //logger.info("Registered new pipe for [" + wildcardPath + ", " + target + "]=(subid="+ newId + ")"); //create initial entry //database.insertNewSubEntry(UUID.fromString(newId), null, null, null, target, null, wildcardPath); } return allok; } else { logger.warning("Model resource null"); return false; } } //check if the newly added publisher should included in any bulk subscription //if so, it add it and returns true, otherwise it does not and returns false public boolean publisherAdded(UUID pubid){ return true; } /** * Publisher removed. Called when a publisher is deleted. Remove all subscriptions * associated with the removed pubid. */ public void pubRemoved(HttpExchange exchange, boolean internalCall, JSONObject internalResp, String pubId){ try { UUID pid = UUID.fromString(pubId); //get each path for every sid and delete the associated rest resource JSONArray sids = database.getSubIdsByPubId(pid); for(int i=0; i<sids.size(); i++){ UUID tsid = UUID.fromString((String)sids.get(i)); String subUri = database.getSubUriBySubId(tsid); Resource r = RESTServer.getResource(subUri); if(r!=null) r.delete(exchange, internalCall, internalResp); } //now remove all of them database.removeSubByPubId(pid); } catch(Exception e){ logger.log(Level.WARNING, "", e); } } /** * Checks that all the streams in the list of streams are registered publishers. All unregistered * pubids are returned. */ public JSONArray checkPubIds(JSONArray pubids){ JSONArray invalidPubIds = new JSONArray(); logger.info("Checking if pubids in list are valid"); for(int i=0; i<pubids.size(); i++){ UUID thisPubid = UUID.fromString((String)pubids.get(i)); if(database.isPublisher(thisPubid)==false) invalidPubIds.add(thisPubid); } return invalidPubIds; } public JSONArray checkPubPaths(JSONArray paths){ JSONArray invalidPubIds = new JSONArray(); logger.info("Checking if pubids in list are valid"); for(int i=0; i<paths.size(); i++){ String thisPath = (String) paths.get(i); Resource r = RESTServer.getResource(thisPath); if(r ==null || (r.TYPE != ResourceUtils.PUBLISHER_RSRC && r.TYPE != ResourceUtils.MODEL_RSRC)) invalidPubIds.add(r); } return invalidPubIds; } /** * Checks if this URL is already in the system. */ public boolean isSubscriber(String sid) { try{ UUID usid = UUID.fromString(sid); return ((database.isSubscription(usid))!=null); }catch(Exception e){ return false; } } /** * Generates a new id. */ private String generateNewId() throws NoMoreRegistrantsException{ //generate a new id String id = UUID.randomUUID().toString(); return id; } /** * Get the publisher id from the data object. */ private UUID getpubid(JSONObject dataObject){ try { Iterator keys = dataObject.keys(); String pubidKey = null; while(keys.hasNext()){ pubidKey = (String)keys.next(); if(pubidKey.equalsIgnoreCase("pubid")) return UUID.fromString(dataObject.getString(pubidKey)); } } catch (Exception e){ logger.warning("No pubid found in data object"); } return null; } /** * Forward this data object to the proper set of subscribers. */ public synchronized void dataReceived(JSONObject dataObject){ logger.fine("dataReceived called: " + dataObject.toString()); try { URL thisSubUrl = null; if(dataObject != null){ //get the pub id from the data object UUID upid = getpubid(dataObject); if(upid == null) return; logger.fine("pid " + upid.toString() + " found!"); //get the list of subscriptions for this publisher streams JSONArray subids = database.getSubIdsByPubId(upid); //enable the information bus resource JSONArray busSubids = database.getSubIdsByPubId(UUID.fromString("00000000-0000-0000-0000-000000000000")); subids.addAll(busSubids); ////////////////////////////////////// logger.info("SubMngr::" + upid.toString() + ":" + subids.toString() + " \tSIZE=" + subids.size()); if(subids != null && subids.size()>0){ //forward this data object to every publisher for(int i=0; i<subids.size(); ++i){ try{ UUID usid = UUID.fromString((String)subids.get(i)); String thisSubUrlStr = database.getSubDestUrlStr(usid); String thisSubUriStr = database.getSubDestUriStr(usid); logger.fine("SubMngr:: URL=" + thisSubUrlStr + "; URI=" + thisSubUriStr); //subscription target is an external url if(thisSubUrlStr != null && thisSubUrlStr.length()>0) { thisSubUrl = new URL(thisSubUrlStr); logger.info("Pushing data to URL: " + thisSubUrl.toString() + " " + dataObject.toString()); if(thisSubUrl != null){ URLConnection urlConn = thisSubUrl.openConnection(); urlConn.setRequestProperty("Content-Type", "application/json"); urlConn.setDoOutput(true); OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream()); wr.write(dataObject.toString()); wr.flush(); BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream())); in.close(); } } //this target is a model, lets see which instance to model thread to forward the data to else if(thisSubUriStr != null){ logger.info("Looking up pipe for subid: " + usid.toString()); String destUri = database.getSubDestUriStr(usid); UUID pubid = database.isPublisher(destUri, false); //lookup the associated pipe and write to it Pipe thisPipe = (Pipe)activePipesByPubId.get(pubid); Semaphore thisSem = (Semaphore)readSemaphoresByPubId.get(pubid); if(thisPipe != null){ logger.info("Associated pipe found: " + usid.toString()); Pipe.SinkChannel sink = thisPipe.sink(); sink.configureBlocking(true); //write dataByteBuf to pipe if(thisPipe.sink().isOpen()){ logger.info(usid.toString() + ": Pipe open, writing to it"); int bytesWritten = writeDataToPipeSink(dataObject, thisSem, sink, usid); if(bytesWritten != dataObject.toString().getBytes().length+4){ logger.warning("Warning: Bytes written not equal to buffer length; " + "bytesWritten=" + bytesWritten + "write Expected=" + dataObject.toString().getBytes().length+4 + " Subscription ID=" + usid.toString()); } } else { logger.info(usid.toString() + ": Pipe closed, reopening"); //the pipe was closed //create new pipe, start new thread, write to pipe Resource model = RESTServer.getResource(thisSubUriStr); if(model.TYPE == ResourceUtils.MODEL_RSRC){ Pipe newPipe = Pipe.open(); Semaphore s = new Semaphore(1, true); ModelResource modelres = (ModelResource)model; Thread modelresInstance = ModelResource.startNewModelThread(s, newPipe.source(),modelres); newPipe.open(); activePipesByPubId.replace(pubid, newPipe); readSemaphoresByPubId.replace(pubid, s); writeDataToPipeSink(dataObject, s, newPipe.sink(), pubid); } else { logger.warning("Resource " + thisSubUriStr + " not a MODEL"); } } } else { //create new pipe for this subscription logger.info(usid.toString() + ": No pipe, opening new one"); //create new pipe, start new thread, write to pipe Resource model = RESTServer.getResource(thisSubUriStr); if(model.TYPE == ResourceUtils.MODEL_RSRC){ Pipe newPipe = Pipe.open(); Semaphore s= new Semaphore(1, true); ModelResource modelres = (ModelResource)model; Thread modelresInstance = ModelResource.restartModelThread(s, newPipe.source(),modelres,pubid); newPipe.open(); activePipesByPubId.replace(pubid, newPipe); readSemaphoresByPubId.replace(pubid, s); writeDataToPipeSink(dataObject, s, newPipe.sink(), pubid); } else { logger.warning("Resource " + thisSubUriStr + " not a MODEL"); } } } //subscription target is a model -- OLD WAY OF DOING IT (BY SUBID) /*else if(thisSubUriStr != null && !thisSubUriStr.contains("*"){ logger.info("Looking up pipe for subid: " + usid.toString()); //lookup the associated pipe and write to it Pipe thisPipe = (Pipe)activePipes.get(usid); Semaphore thisSem = (Semaphore)readSemaphores.get(usid); if(thisPipe != null){ logger.info("Associated pipe found: " + usid.toString()); Pipe.SinkChannel sink = thisPipe.sink(); sink.configureBlocking(true); //write dataByteBuf to pipe if(thisPipe.sink().isOpen()){ logger.info(usid.toString() + ": Pipe open, writing to it"); int bytesWritten = writeDataToPipeSink(dataObject, thisSem, sink, usid); if(bytesWritten != dataObject.toString().getBytes().length+4){ logger.warning("Warning: Bytes written not equal to buffer length; " + "bytesWritten=" + bytesWritten + "write Expected=" + dataObject.toString().getBytes().length+4 + " Subscription ID=" + usid.toString()); } } else { logger.info(usid.toString() + ": Pipe closed, reopening"); //the pipe was closed //create new pipe, start new thread, write to pipe Resource model = RESTServer.getResource(thisSubUriStr); if(model.TYPE == ResourceUtils.MODEL_RSRC){ Pipe newPipe = Pipe.open(); Semaphore s = new Semaphore(1, true); ModelResource modelres = (ModelResource)model; Thread modelresInstance = ModelResource.startNewModelThread(s, newPipe.source(),modelres); newPipe.open(); activePipes.replace(usid, newPipe); readSemaphores.replace(usid, s); writeDataToPipeSink(dataObject, s, newPipe.sink(), usid); } else { logger.warning("Resource " + thisSubUriStr + " not a MODEL"); } } } else { //create new pipe for this subscription logger.info(usid.toString() + ": No pipe, opening new one"); //create new pipe, start new thread, write to pipe Resource model = RESTServer.getResource(thisSubUriStr); if(model.TYPE == ResourceUtils.MODEL_RSRC){ Pipe newPipe = Pipe.open(); Semaphore s= new Semaphore(1, true); ModelResource modelres = (ModelResource)model; Thread modelresInstance = ModelResource.restartModelThread(s, newPipe.source(),modelres,usid); newPipe.open(); activePipes.replace(usid, newPipe); readSemaphores.replace(usid, s); writeDataToPipeSink(dataObject, s, newPipe.sink(), usid); } else { logger.warning("Resource " + thisSubUriStr + " not a MODEL"); } } }*/ else { logger.info("Could not find target for subid: " + usid.toString()); } } catch(Exception e){ logger.log(Level.WARNING, "Error while forwarding data", e); if(e instanceof ConnectException){ logger.log(Level.WARNING, "Could not connect to subscriber", e); } else if(e instanceof InstantiationException){ logger.warning(e.getMessage()); } } } } } } catch (Exception e){ logger.log(Level.WARNING, "", e); } } private static int writeDataToPipeSink(JSONObject dataObj, Semaphore s, Pipe.SinkChannel sink, UUID modelPubId){ try { //write json object to byte buffer (prepended by buffer length) byte[] data_obj_buf = (dataObj.toString().trim() + "\n").getBytes(); int bufferLength = data_obj_buf.length + 4; byte[] data= new byte[bufferLength]; ByteBuffer dataByteBuf = ByteBuffer.wrap(data); //write the length of the data section followed by the data dataByteBuf = dataByteBuf.putInt(bufferLength); dataByteBuf = dataByteBuf.put(data_obj_buf); //write dataByteBuf to pipe if(sink.isOpen()){ dataByteBuf.rewind(); int bytesWritten = sink.write(dataByteBuf); if(bytesWritten != dataByteBuf.array().length){ logger.warning("Warning: Bytes written less then buffer length; Model_Publisher ID=" + modelPubId.toString()); } logger.finer("SUBMNGR:BEFORE_LEASE_COUNT=" + s.availablePermits()); s.release(); logger.finer("SUBMNGR:AFTER_LEASE_COUNT=" + s.availablePermits()); return bytesWritten; } } catch(Exception e){ logger.log(Level.WARNING, "",e); } return 0; } public void removeSub(UUID subid){ try { String destStr = database.getSubDestUriStr(subid); logger.info("Removing subid: " + subid.toString() + "\n\tdestStr=" + destStr + "\n\tsub_count=" + database.getSubCountToModelPub(destStr)); if(destStr != null && destStr.startsWith("/models") && database.getSubCountToModelPub(destStr)==1){ UUID mpubid = database.isRRPublisher2(destStr); logger.info("\n\tpubid_found=" + mpubid + "\n\tinternal_pipe_found=" + activePipesByPubId.containsKey(mpubid)); if(mpubid!=null && activePipesByPubId.containsKey(mpubid)){ logger.fine("Sending kill command to thread-" + mpubid.toString()); Pipe p = activePipesByPubId.get(mpubid); Pipe.SinkChannel sink = p.sink(); //send kill op to thread JSONObject c = new JSONObject(); c.put("operation", "kill"); ByteBuffer dataByteBuf = ByteBuffer.wrap(c.toString().getBytes()); dataByteBuf.rewind(); int v = sink.write(dataByteBuf); Thread.yield(); sink.close(); //signal message ready Semaphore s = readSemaphoresByPubId.get(mpubid); if(s!=null) s.release(); //remove internal pub and semaphore activePipesByPubId.remove(mpubid); readSemaphoresByPubId.remove(mpubid); Resource r = RESTServer.getResource(destStr); //remove the associated publisher resource RESTServer.removeResource(r); //remove the publisher entry database.removePublisher(mpubid); database.removeRestResource(r.getURI()); //remove from internal graph Resource.removeFromMetadataGraph(r.getURI()); } } } catch (Exception e){ logger.log(Level.WARNING, "",e); } //remove all subscriber from table database.removeSubEntry(subid); } public void signalModelThreadKill(Resource r){ logger.fine("Thread-kill signaled from external resource"); try { if(r !=null && r instanceof ModelGenericPublisherResource){ UUID mpubid = database.isRRPublisher2(r.getURI()); if(mpubid!=null && activePipesByPubId.containsKey(mpubid)){ logger.fine("Sending kill command to thread-" + mpubid.toString()); Pipe p = activePipesByPubId.get(mpubid); Pipe.SinkChannel sink = p.sink(); //send kill op to thread JSONObject c = new JSONObject(); c.put("operation", "kill"); ByteBuffer dataByteBuf = ByteBuffer.wrap(c.toString().getBytes()); dataByteBuf.rewind(); int v = sink.write(dataByteBuf); Thread.yield(); sink.close(); //signal message ready Semaphore s = readSemaphoresByPubId.get(mpubid); if(s!=null) s.release(); //remove internal pub and semaphore activePipesByPubId.remove(mpubid); readSemaphoresByPubId.remove(mpubid); } else { logger.info("No associated pubid found for " + r.getURI()); } } } catch(Exception e){ logger.log(Level.WARNING, "", e); } } }