UPnP.java :  » Media » jems » net » kodeninja » UPnP » Java Open Source

Java Open Source » Media » jems 
jems » net » kodeninja » UPnP » UPnP.java
package net.kodeninja.UPnP;

import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import net.kodeninja.UPnP.description.RootDevice;
import net.kodeninja.UPnP.description.Service;
import net.kodeninja.UPnP.description.ServiceStateVariable;
import net.kodeninja.UPnP.identifiers.SSDPIdentifier;
import net.kodeninja.UPnP.identifiers.SSDPUUID;
import net.kodeninja.UPnP.internal.control.ControlActionURI;
import net.kodeninja.UPnP.internal.control.ControlQueryURI;
import net.kodeninja.UPnP.internal.description.DeviceDescriptionURI;
import net.kodeninja.UPnP.internal.description.ServiceDescriptionURI;
import net.kodeninja.UPnP.internal.discovery.DiscoveryNotifyHandler;
import net.kodeninja.UPnP.internal.discovery.MSearchHandler;
import net.kodeninja.UPnP.internal.discovery.MSearchResponseHandler;
import net.kodeninja.UPnP.internal.eventing.EventSubscribeURI;
import net.kodeninja.UPnP.internal.eventing.EventUnsubscribeURI;
import net.kodeninja.UPnP.internal.eventing.Subscription;
import net.kodeninja.http.service.HTTPSocket;
import net.kodeninja.http.service.HTTPTCPService;
import net.kodeninja.http.service.HTTPUDPService;
import net.kodeninja.http.service.handlers.GetHandler;
import net.kodeninja.http.service.handlers.PacketHeaderLogger;
import net.kodeninja.http.service.handlers.PageRequestHandler;
import net.kodeninja.http.service.handlers.PostHandler;
import net.kodeninja.scheduling.MinimumTimeJob;
import net.kodeninja.scheduling.Scheduler;
import net.kodeninja.util.Pair;
import net.kodeninja.util.logging.LoggerCollection;

public final class UPnP extends LoggerCollection {

  public static final String SERVICE_ROOT_DEVICE = "upnp:rootdevice";
  public static InetAddress SSDP_MULTICAST;
  public static URI STAR_URI;

  private static UPnP instance = null;

  private HTTPUDPService notifyService, broadcastService;
  private HTTPTCPService descriptionService;
  private Scheduler sched;
  private Map<SSDPUUID, Set<UPnPCache>> cache = new HashMap<SSDPUUID, Set<UPnPCache>>();
  private Set<UPnPBrowseOperation> browsers = new HashSet<UPnPBrowseOperation>();
  private Set<UPnPAdvertiseOperation> advertisers = new HashSet<UPnPAdvertiseOperation>();
  private Map<Service, Map<SSDPUUID, Subscription>> subscriptions = new HashMap<Service, Map<SSDPUUID, Subscription>>();
  private Map<Service, Subscription> newSubs = new HashMap<Service, Subscription>();
  private Map<Pair<HTTPSocket, SSDPIdentifier>, Long> queuedResponses = new HashMap<Pair<HTTPSocket, SSDPIdentifier>, Long>();
  private UPnPPollingThread pollingThread;
  private boolean doLogging = false;

  class UPnPPollingThread extends MinimumTimeJob {
    private UPnP host;

    UPnPPollingThread(UPnP host) {
      super(true, host.getScheduler(), 500, false);
      this.host = host;
    }

    public void run() {
      host.checkCacheExpiry();
      host.checkAdvertisement();
      host.checkSubscriptions();
      host.checkQueuedResponses();
      super.run();
    }
  }

  private UPnP(LoggerCollection loggerCollection, Scheduler s, boolean verboseLogging) {
    //Make sure all the ads are stopped when program is stopped.
    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        shutdown();
      }});

    doLogging = verboseLogging;

    if (loggerCollection != null)
      this.addLogger(loggerCollection);

    sched = s;

    try {
      SSDP_MULTICAST = InetAddress.getByName("239.255.255.250");
      STAR_URI = new URI("*");
    }
    catch (Exception e) {
      e.printStackTrace();
      // This should never be reached.
    }

    if (sched == null) {
      notifyService = new HTTPUDPService(SSDP_MULTICAST, 1900, 3);
      sched = notifyService.getScheduler();
    }
    else
      notifyService = new HTTPUDPService(sched, SSDP_MULTICAST, 1900);
    if (doLogging)
      notifyService.getTransport().addHandler(new PacketHeaderLogger(this, true));
    notifyService.getTransport().addHandler(new DiscoveryNotifyHandler(this));
    notifyService.getTransport().addHandler(new MSearchHandler(this));
    notifyService.getTransport().addHandler(new PacketHeaderLogger(this, true));
    notifyService.addLogger(this);
    notifyService.getTransport().setServerString(UPnP.getServerString());
    notifyService.getTransport().setTTL(4);

    broadcastService = new HTTPUDPService(sched, UPnP.SSDP_MULTICAST, -1);
    if (doLogging)
      broadcastService.getTransport().addHandler(new PacketHeaderLogger(this, true));
    broadcastService.getTransport().addHandler(new MSearchResponseHandler(this));
    broadcastService.getTransport().addHandler(new PacketHeaderLogger(this, true));
    broadcastService.addLogger(this);
    broadcastService.getTransport().setServerString(UPnP.getServerString());
    broadcastService.getTransport().setTTL(4);

    GetHandler descriptionHandler = new GetHandler();
    descriptionHandler.enableChunked(false);
    descriptionHandler.enableByteRange(false);
    descriptionHandler.addURIHandler(new DeviceDescriptionURI(this));
    descriptionHandler.addURIHandler(new ServiceDescriptionURI(this));

    PostHandler controlHandler = new PostHandler();
    controlHandler.enableChunked(false);
    controlHandler.enableByteRange(false);
    controlHandler.addURIHandler(new ControlActionURI(this));
    controlHandler.addURIHandler(new ControlQueryURI(this));

    PageRequestHandler subscriptionHandler = new PageRequestHandler();
    subscriptionHandler.enableChunked(false);
    subscriptionHandler.enableByteRange(false);
    subscriptionHandler.addURIHandler(new EventSubscribeURI(this));
    subscriptionHandler.addURIHandler(new EventUnsubscribeURI(this));

    descriptionService = new HTTPTCPService(sched, -1);
    if (doLogging)
      descriptionService.getTransport().addHandler(new PacketHeaderLogger(this, true));
    descriptionService.getTransport().addHandler(descriptionHandler);
    descriptionService.getTransport().addHandler(controlHandler);
    descriptionService.getTransport().addHandler(subscriptionHandler);
    descriptionService.getTransport().addHandler(new PacketHeaderLogger(this, true));
    descriptionService.getTransport().setServerString(UPnP.getServerString());
    descriptionService.addLogger(this);


    //Start services
    notifyService.start();
    broadcastService.start();
    descriptionService.start();

    if (doLogging) {
      addLog("UPnP: Notify Service Started on port " + notifyService.getBoundPort());
      addLog("UPnP: Broadcast Service Started on port " + broadcastService.getBoundPort());
      addLog("UPnP: Description Service Started on port " + descriptionService.getBoundPort());
    }

    //Start polling thread
    pollingThread = new UPnPPollingThread(this);
    pollingThread.start();
  }

  private synchronized void shutdown() {
    instance = null;
    Set<UPnPAdvertiseOperation> oldAds = advertisers;
    advertisers = null;
    for (UPnPAdvertiseOperation ad: oldAds)
      ad.stop();
  }

  private synchronized static UPnP getInstance() {
    if (instance == null)
      instance = new UPnP(null, null, false);
    return instance;
  }

  public synchronized static void init(LoggerCollection loggerCollection, Scheduler sched, boolean verboseLogging) {
    if (instance == null)
      instance = new UPnP(loggerCollection, sched, verboseLogging);
    else
      instance.doLogging = verboseLogging;
  }

  public Scheduler getScheduler() {
    return sched;
  }

  public HTTPUDPService getBroadcastService() {
    return broadcastService;
  }

  private synchronized void checkCacheExpiry() {
    for (Set<UPnPCache> identSet : cache.values())
      for (UPnPCache ident : identSet)
        if (ident.hasExpired()) {
          identSet.remove(ident);
          if (doLogging)
            notifyService.addLog("REMOVED: Removed '" + ident + "' from uuid " + ident.getUUID() + " due to expiry.");
          break;
        }
  }

  private synchronized void checkQueuedResponses() {
    Iterator<Pair<HTTPSocket, SSDPIdentifier>> it = queuedResponses.keySet().iterator();
    long time = System.currentTimeMillis();

    while (it.hasNext()) {
      Pair<HTTPSocket, SSDPIdentifier> ad = it.next();
      Long queuedTime = queuedResponses.get(ad);
      if (queuedTime < time) {
        sendAdvertisementOn(ad.getA(), ad.getB());
        it.remove();
      }
    }
  }

  private synchronized void checkAdvertisement() {
    if (advertisers != null)
      for (UPnPAdvertiseOperation ad: advertisers) {
        if (ad.resendAdvertisement())
          ad.sendAdvertisement();
      }
  }

  private void checkSubscriptions() {
    Map<Service, Subscription> oldSubs = null;

    synchronized (this) {
      oldSubs = newSubs;
      newSubs = new HashMap<Service, Subscription>();
    }

    for (Service s: oldSubs.keySet()) {
      Subscription sub = oldSubs.get(s);
      Map<ServiceStateVariable, Object> firstEvent = new HashMap<ServiceStateVariable, Object>();

      Iterator<ServiceStateVariable> varIt = s.stateVariables();
      while (varIt.hasNext()) {
        ServiceStateVariable var = varIt.next();
        if (var.sendsEvents())
          firstEvent.put(var, s.getStateVarValue(var));
      }

      sub.announceStateChange(firstEvent);
    }
    oldSubs.clear();

    Iterator<Service> serviceIt = subscriptions.keySet().iterator();
    while (serviceIt.hasNext()) {
      Service service = serviceIt.next();
      Map<SSDPUUID, Subscription> sidMapping = subscriptions.get(service);
      Iterator<SSDPUUID> sidIt = sidMapping.keySet().iterator();

      while (sidIt.hasNext())
        if (sidMapping.get(sidIt.next()).expired())
          sidIt.remove();

      if (sidMapping.size() == 0)
        serviceIt.remove();
    }
  }

  public synchronized void queueAdvertisement(HTTPSocket Socket, SSDPIdentifier ident, long time) {
    Pair<HTTPSocket, SSDPIdentifier> ad = new Pair<HTTPSocket, SSDPIdentifier>(Socket, ident);
    queuedResponses.put(ad, new Long(time));
  }

  private synchronized void sendAdvertisementOn(HTTPSocket Socket, SSDPIdentifier ident) {
    if (advertisers != null)
      for (UPnPAdvertiseOperation ad: advertisers) {
        ad.sendAdvertisementOn(Socket, ident);
      }
  }

  public synchronized void addToCache(SSDPUUID uuid, UPnPCache ident) {
    Set<UPnPCache> identSet = cache.get(uuid);
    if (identSet == null) {
      identSet = new HashSet<UPnPCache>();
      cache.put(uuid, identSet);
      if (doLogging)
        notifyService.addLog("UUID POOL: Added " + uuid);
    }

    if (identSet.remove(ident) == false) {
      if (doLogging)
        notifyService.addLog("NEW: Added '" + ident + "' to uuid " + uuid);
      for (UPnPBrowseOperation browse : browsers)
        browse.added(ident);
    }
    else if (doLogging)
      notifyService.addLog("UPDATED: Replaced '" + ident + "' on uuid " + uuid);
    identSet.add(ident);
  }

  public synchronized void removeFromCache(SSDPUUID uuid, UPnPCache ident) {
    Set<UPnPCache> identSet = cache.get(uuid);
    if (identSet == null) {
      if (doLogging)
        notifyService.addLog("MISSING UUID: Unannounced uuid removing ident '" + ident + "' from uuid " + uuid);
      return;
    }
    if (identSet.remove(ident)) {
      if (doLogging)
        notifyService.addLog("REMOVED: Removed '" + ident + "' from uuid " + uuid + " due to BYEBYE.");
      for (UPnPBrowseOperation browse : browsers)
        browse.removed(ident);
    }
    else if (doLogging)
      notifyService.addLog("MISSING IDENT: Unannounced ident '" + ident + "' from uuid " + uuid);

    if (identSet.isEmpty()) {
      if (doLogging)
        notifyService.addLog("UUID POOL: Removed " + uuid + " due to empty set.");
      cache.remove(uuid);
    }
  }

  private synchronized void addBrowser(UPnPBrowseOperation browser) {
    browsers.add(browser);
  }

  public synchronized void removeBrowser(UPnPBrowseOperation browser) {
    browsers.remove(browser);
  }

  public static UPnPBrowseOperation browse(SSDPIdentifier ident, UPnPBrowseListener listener) {
    UPnPBrowseOperation retVal = new UPnPBrowseOperation(getInstance(), ident, listener);
    getInstance().addBrowser(retVal);

    return retVal;
  }

  private synchronized void addAdvertiser(UPnPAdvertiseOperation advertiser) {
    if (advertisers != null)
      advertisers.add(advertiser);
  }

  public synchronized void removeAdvertiser(UPnPAdvertiseOperation advertiser) {
    if (advertisers != null)
      advertisers.remove(advertiser);
  }

  public UPnPAdvertiseOperation getAdvertisterByRootUUID(SSDPUUID UUID) {
    if (advertisers != null)
      for (UPnPAdvertiseOperation ad: advertisers)
        if (ad.matches(UUID))
          return ad;
    return null;
  }

  public static UPnPOperation advertise(RootDevice d, UPnPAdvertiseListener advertiser) {
    UPnPAdvertiseOperation retVal = new UPnPAdvertiseOperation(getInstance(), d);
    getInstance().addAdvertiser(retVal);
    retVal.sendAdvertisement();
    return retVal;
  }

  public static String getFullName() {
    return "KodeNinja UPnP Stack/0.1";
  }

  public static String getUserAgentString() {
    return getFullName().replace(' ', '-') + " (compatible; UPnP/1.0; " +
    System.getProperty("os.name") + "/" +
    System.getProperty("os.version") + ")";
  }

  public static String getServerString() {
    return System.getProperty("os.name").replace(' ', '-') + "/" +
    System.getProperty("os.version").replace(' ', '-') +
    " UPnP/1.0 " + getFullName().replace(' ', '-');
  }

  public static String getURLBase() {
    return UPnP.getInstance().descriptionService.getURLBase() + getURIBase();
  }

  public static String getURIBase() {
    return "/upnp/";
  }

  public synchronized SSDPUUID addSubscription(Service s, URL callback, long timeout) {
    pollingThread.reset();

    SSDPUUID sid = new SSDPUUID(UUID.randomUUID().toString());

    Map<SSDPUUID, Subscription> serviceSubscriptions = subscriptions.get(s);

    if (serviceSubscriptions == null) {
      serviceSubscriptions = new HashMap<SSDPUUID, Subscription>();
      subscriptions.put(s, serviceSubscriptions);
    }

    Subscription sub = new Subscription(s, sid, callback, timeout);
    serviceSubscriptions.put(sid, sub);

    newSubs.put(s, sub);

    return sid;
  }

  public synchronized boolean renewSubscription(Service s, SSDPUUID sid, long timeout) {
    Map<SSDPUUID, Subscription> mapping = subscriptions.get(s);
    if (mapping != null) {
      Subscription sub = mapping.get(sid);
      if (sub != null) {
        sub.renew(timeout);
        return true;
      }
    }
    return false;
  }

  public synchronized void removeSubscription(Service s, SSDPUUID sid) {
    Map<SSDPUUID, Subscription> serviceSubscriptions = subscriptions.get(s);

    if (serviceSubscriptions == null)
      return;

    serviceSubscriptions.remove(sid);
    if (serviceSubscriptions.isEmpty())
      subscriptions.remove(s);
  }

  public static void announceStateChange(Service s, ServiceStateVariable var, Object val) {
    Map<ServiceStateVariable, Object> tmp = new HashMap<ServiceStateVariable, Object>();
    tmp.put(var, val);
    announceStateChange(s, tmp);
  }

  public static void announceStateChange(Service s, Map<ServiceStateVariable, Object> vals) {
    Map<SSDPUUID, Subscription> mapping = null;
    if (instance == null)
      return;
    synchronized (instance) {
      mapping = instance.subscriptions.get(s);
      if (mapping == null)
        return;
    }
    for (Subscription sub: mapping.values())
      sub.announceStateChange(vals);
  }

  //Update getFullName when this changes
  public String getName() {
    return "KodeNinja UPnP Stack";
  }

  //Update getFullName when this changes
  public int getVersionMajor() {
    return 0;
  }

  //Update getFullName when this changes
  public int getVersionMinor() {
    return 1;
  }

  public int getVersionRevision() {
    return 0;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.