/*
* "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 is4;
//import java.net.*;
import java.net.URL;
import java.util.UUID;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.Vector;
//import java.util.*;
import java.io.*;
import java.util.logging.Logger;
import java.util.logging.Level;
/**
* Manages the addition and removal of subscribers
*/
public class SubscriberTable implements Serializable{
//subscriber name to subscriber object mapping
protected Hashtable<String, Subscriber> subnameTable = new Hashtable<String, Subscriber>();
//subscriber ID to subscriber object mapping
protected Hashtable<UUID, Subscriber> subTable = new Hashtable<UUID, Subscriber>();
//publisher ID to subscriber list-object mapping
protected Hashtable<UUID, List<Subscriber>> streamTable = new Hashtable<UUID, List<Subscriber>>();
//hash to keep track of URLs
protected HashMap<URL, Subscriber> urlMap = new HashMap<URL, Subscriber>();
//Logging
protected static transient final Logger localLogger =Logger.getLogger(SubscriberTable.class.getPackage().getName());
//Path to serialized object
protected static String thisTableObjPath = "/subscriberTable.obj";
protected static String subTableObjPath = "/subtable.obj";
protected static String subnameTableObjPath = "/subnametable.obj";
protected static String streamTableObjPath = "/streamtable.obj";
protected static String urlMapObjPath = "/urlmap.obj";
public SubscriberTable(){
}
/**
* Returns a previously saved SubscriberTable instance or returns
* a new instance of a SubscriberTable object. The path MUST point to the
* folder/directory where the object was previously saved.
*
* @param path path to folder or directory where the object is saved.
* @returns new SubscriberTable instance or the instance saved in the given folder.
*/
public static SubscriberTable loadSubscriberTable(String path){
SubscriberTable sTable = null;
FileInputStream fileIn = null;
ObjectInputStream in = null;
SubscriberTable subscriberTable = new SubscriberTable();
Logger thisLogger = subscriberTable.localLogger;
thisLogger.info("Loading subscriber table form " + path);
try {
//check for SubscriberTable object existence
File f1 = new File(path + thisTableObjPath);
//System.out.println("1: " + path + thisTableObjPath);
if(f1.exists()){
fileIn = new FileInputStream(f1);
in = new ObjectInputStream(fileIn);
sTable = (SubscriberTable)in.readObject();
thisLogger.fine("Subscriber table loaded");
}else {
sTable = new SubscriberTable();
thisLogger.fine("Subscriber table instantiated");
}
f1 = new File(path + subTableObjPath);
//System.out.println("2: " + path + subTableObjPath);
if(f1.exists()){
fileIn = new FileInputStream(f1);
in = new ObjectInputStream(fileIn);
sTable.subTable = (Hashtable<UUID, Subscriber>)in.readObject();
thisLogger.fine("Subscriber uuid table set and loaded in SubscriberTable Instance");
}
f1 = new File(path + subnameTableObjPath);
//System.out.println("2: " + path + subTableObjPath);
if(f1.exists()){
fileIn = new FileInputStream(f1);
in = new ObjectInputStream(fileIn);
sTable.subnameTable = (Hashtable<String, Subscriber>)in.readObject();
thisLogger.fine("Subscriber name table set and loaded in SubscriberTable Instance");
}
f1 = new File(path + streamTableObjPath);
//System.out.println("3: " + path + streamTableObjPath);
if(f1.exists()){
fileIn = new FileInputStream(f1);
in = new ObjectInputStream(fileIn);
sTable.streamTable = (Hashtable<UUID, List<Subscriber>>)in.readObject();
thisLogger.fine("Stream table set and loaded in SubscriberTable Instance");
}
f1 = new File(path + urlMapObjPath);
//System.out.println("4: " + path + urlMapObjPath);
if(f1.exists()){
fileIn = new FileInputStream(f1);
in = new ObjectInputStream(fileIn);
sTable.urlMap = (HashMap<URL, Subscriber>)in.readObject();
thisLogger.fine("Url map set and loaded in SubscriberTable Instance");
}
} catch(Exception e) {
thisLogger.log(Level.SEVERE, "SubscriberTable: Error Loading SubscriberTable; Exiting!", e);
System.exit(1);
//System.err.print("SubscriberTable: Error Loading SubscriberTable");
//e.printStackTrace();
}
return sTable;
}
/**
* Save this object and associated sub-Objects in the given path.
*/
public void saveObject(String path) {
localLogger.info("Saving object in " + path);
try {
FileOutputStream fileOut = new FileOutputStream(path + thisTableObjPath);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
SubscriberTable thisTable = new SubscriberTable();
thisTable.subTable = this.subTable;
thisTable.streamTable = this.streamTable;
thisTable.urlMap = this.urlMap;
out.writeObject(thisTable);
localLogger.fine("Writing Subscriber table to disk");
fileOut = new FileOutputStream(path + subTableObjPath);
out = new ObjectOutputStream(fileOut);
out.writeObject(subTable);
localLogger.fine("Writing Subscriber UUID hash table to disk");
fileOut = new FileOutputStream(path + subnameTableObjPath);
out = new ObjectOutputStream(fileOut);
out.writeObject(subnameTable);
localLogger.fine("Writing Subscriber name hash table to disk");
fileOut = new FileOutputStream(path + streamTableObjPath);
out = new ObjectOutputStream(fileOut);
out.writeObject(streamTable);
localLogger.fine("Writing Stream hash table to disk");
fileOut = new FileOutputStream(path + urlMapObjPath);
out = new ObjectOutputStream(fileOut);
out.writeObject(urlMap);
localLogger.fine("Writing url map to disk");
//System.out.println("SubscriberTable: Wrote objects");
} catch(Exception e) {
localLogger.log(Level.WARNING, "SubscriberTable: Error(s) while saving objects",e);
//System.err.print("SubscriberTable: Error(s) while saving objects");
//e.printStackTrace();
}
}
//put subscriber in table
public synchronized void add(Subscriber s){
try{
localLogger.fine("Adding a subcriber:\n" + s.toString());
ArrayList<String> streamIds = (ArrayList<String>)s.getSubStreamIds();
localLogger.info("s.|StreamdIds| = " + streamIds.size());
//add subscriber to list of subscribers for this publisher
localLogger.fine("Checking each publisher's subscriber list and adding this subscriber if it's not on the associated list");
for(int i=0; i<streamIds.size(); i++){
String pubId = streamIds.get(i);
localLogger.info("StreamId[" + i + "]= " + pubId);
if(pubId != null){
ArrayList<Subscriber> theseSubs = (ArrayList<Subscriber>) streamTable.get(UUID.fromString(pubId));
if(theseSubs != null){
if(!theseSubs.contains(s)) {
theseSubs.add(s);
} else {
localLogger.warning("Already a subscriber to " + pubId +
", NOT ADDING");
}
}else {
//System.out.println("No subscribers, adding...");
localLogger.info("No subscribers, adding a new subscriber");
ArrayList<Subscriber> subscriberList = new ArrayList<Subscriber>();
subscriberList.add(s);
//add to each table/map
streamTable.put(UUID.fromString(pubId), subscriberList);
}
}
else{
//System.out.println("PUBLISHER IN LIST IS NULL");
localLogger.warning("Publisher in list null");
}
}
} catch (IllegalArgumentException e) {
localLogger.log(Level.WARNING, "Unexpected illegal publisher id in list", e);
}
//now add subscriber to table of subscribers
if(!subTable.containsKey(s.getSubId())){
//System.out.println("adding subid" + s.getSubId());
localLogger.fine("Adding subid " + s.getSubId());
subTable.put(UUID.fromString(s.getSubId()), s);
}
//register the URL (or URL of the proxy), these have to be unique as well
if(s.usesProxy() ){//&& !urlMap.containsKey(s.getProxyUrl())){
String p = s.getProxyUrl().toString() + "?random=" +UUID.randomUUID().toString();
try{
URL pUrl = new URL(p);
localLogger.fine("Registering proxy url: " + pUrl.toString());
urlMap.put(pUrl,s);
}
catch(Exception e){
localLogger.log(Level.WARNING, "", e);
}
} else if(!urlMap.containsKey(s.getSubUrl())){
//System.out.println("Registering url: "+ s.getSubUrl() + " " + s.usesProxy());
localLogger.fine("Registering url: "+ s.getSubUrl() + " " + s.usesProxy());
urlMap.put(s.getSubUrl(), s);
}
//saveObjects();
}
/**
* Publisher has unregistered (i.e. publisher-to-subscriber_list mapping is removed).
*/
public synchronized void publisherRemoved(String pubId){
try {
localLogger.info("Removing " + pubId);
//Long pubIdLong = new Long(pubId);
UUID id = UUID.fromString(pubId);
List<Subscriber> associatedSubList = streamTable.remove(id);
if(associatedSubList != null)
for(int i=0; i<associatedSubList.size(); i++)
associatedSubList.get(i).removeStream(pubId);
localLogger.info("Removed id " + pubId + " from SubscriberTable");
} catch (IllegalArgumentException e){
localLogger.log(Level.WARNING, "Cannot remove " + pubId + "; invalid UUID format", e);
}
}
/**
* Remove subscriber from subscriber list.
*/
public synchronized void remove(Subscriber s){
int check=0;
localLogger.info("Removing subscriber");
if(s!=null){
localLogger.fine(s.toString());
}
else {
localLogger.warning("Trying to remove a null subscriber");
return;
}
try {
if (s != null){
subTable.remove(s.getSubId());
ArrayList<String> streamIds = (ArrayList<String>)s.getSubStreamIds();
//remove the url for the subscriber or the proxy
if(s.usesProxy()){
urlMap.remove(s.getProxyUrl());
}
else{
urlMap.remove(s.getSubUrl());
}
//remove the subscriber from the list of subscribers for all publishers to which
//this subscriber has a subscription to
check =1;
for(int i=0; i<streamIds.size(); ++i){
localLogger.finer("Getting the subcriber list for publisher " + streamIds.get(i));
List<Subscriber> subscriberList = streamTable.get(UUID.fromString(streamIds.get(i)));
if(subscriberList != null){
ArrayList<Subscriber> thisList = (ArrayList<Subscriber>) subscriberList;
if(thisList.contains(s)){
localLogger.finest("Removing " + s.getSubId() + " from Susbcriber list: " + thisList.toString());
//streamTable.remove(streamIds.get(i));
thisList.remove(s);
//streamTable.put(UUID.fromString(streamIds.get(i)), thisList);
}
}
}
check =2;
//remove from the other three tables
subTable.remove(UUID.fromString(s.getSubId()));
if(s.getName()!=null)
subnameTable.remove(s.getName());
urlMap.remove(s.getSubUrl());
}
} catch(IllegalArgumentException e) {
if(check==1){
localLogger.log(Level.WARNING, "Could not remove " + s.getSubId() + "; Unexpected illegal UUID in stream Ids list", e);
} else {
localLogger.log(Level.WARNING, "Could not remove " + s.getSubId() + "; Illegal UUID String", e);
}
}
}
public synchronized void addPubToSubList(String pid, String sid){
try {
Registrar R = Registrar.registrarInstance();
if(R.isRegisteredId(pid)){
//add to subscriber list
Subscriber s = this.getSubscriber(sid);
s.addStream(pid);
//add subscriber to sub list for this pub
UUID PID = UUID.fromString(pid);
if(streamTable.containsKey(PID)){
List<Subscriber> subList = streamTable.get(PID);
//DEBUG
System.out.print("Trying to add subscriber_id=" + sid);
for(int g=0; g<subList.size(); g++){
System.out.println("sublist[" + g + "]: " +
subList.get(g).getSubId());
}
//////////////////////////
if(!subList.contains(s))
subList.add(s);
} else {
ArrayList<Subscriber> subList = new ArrayList<Subscriber>();
subList.add(s);
streamTable.put(PID,subList);
}
}
} catch (IllegalArgumentException e) {
localLogger.log(Level.WARNING, "Invalid publisher id " + pid + ", or subscriber id " + sid);
}
}
public synchronized void removePubFromSubList(String pid, String sid){
try{
Registrar R = Registrar.registrarInstance();
UUID PID = UUID.fromString(pid);
UUID SID = UUID.fromString(sid);
if(R.isRegisteredId(pid) && streamTable.containsKey(PID)){
List<Subscriber> subList = (List<Subscriber>) streamTable.get(PID);
//remove to subscriber list
Subscriber s = subTable.get(SID);
if(s!=null){
subList.remove(s);
s.removeStream(pid);
}
}
} catch (IllegalArgumentException e) {
localLogger.log(Level.WARNING, "Could not remove pub " + pid + " from subscriber " + sid + " list", e);
}
}
public List<Subscriber> getAllSubs(String publisherId){
try {
UUID pid = UUID.fromString(publisherId);
return streamTable.get(pid);
} catch(IllegalArgumentException e) {
localLogger.log(Level.WARNING, "Invalid stream identifier format: " + publisherId + "; invalid UUID", e);
return (List<Subscriber>) new ArrayList<Subscriber>();
}
}
public List<String> getAllStreams(String subId){
try {
UUID subid = UUID.fromString(subId);
Subscriber s = subTable.get(subid);
if(s!=null){
localLogger.info("Subscriber " + subId + " found in SubscriberTable");
return s.getSubStreamIds();
} else {
localLogger.info("No subscriber " + subId + " in SubscriberTable");
}
} catch (IllegalArgumentException e){
localLogger.log(Level.WARNING, "Invalid stream identifier format: " + subId + "; invalid UUID", e);
}
return null;
}
public List<Subscriber> getRegisteredSubscribers(){
Collection<Subscriber> sublist_ = (Collection<Subscriber>) subTable.values();
if(sublist_ != null)
return new ArrayList<Subscriber>(sublist_);
return null;
}
public List<String> getRegisteredSubscriberIds(){
Collection<UUID> sublist_ = (Collection<UUID>) subTable.keySet();
Vector<UUID> sublistVec = new Vector<UUID>(sublist_);
Collection<String> sublistStr = (Collection<String>) new Vector<String>(subTable.size());
if(sublist_ != null){
for(int i=0; i<sublist_.size(); i++)
sublistStr.add(sublistVec.get(i).toString());
return new ArrayList<String>(sublistStr);
}
return null;
}
public Subscriber getSubscriber(String id){
try {
UUID thisId = UUID.fromString(id);
return subTable.get(thisId);
} catch (IllegalArgumentException e) {
localLogger.log(Level.WARNING, "Invalid subscriber identifier format: " + id + "; invalid UUID", e);
return null;
}
}
public Subscriber getSubscriber(URL url){
return urlMap.get(url);
}
//membership
public boolean containsUrl(URL url){
return urlMap.containsKey(url);
}
public boolean containsSubId(String subId){
try {
//System.out.println("sid="+subId);
//System.out.println("keyset:"+subTable.keySet());
UUID id = UUID.fromString(subId);
return subTable.containsKey(id);
} catch (IllegalArgumentException e) {
localLogger.log(Level.WARNING, "Invalid subscriber identifier format: " + subId + "; invalid UUID", e);
return false;
}
}
}