Package com.aelitis.azureus.core.devices.impl

Source Code of com.aelitis.azureus.core.devices.impl.DeviceTivoManager$Searcher

/*
* Created on Jul 24, 2009
* Created by Paul Gardner
*
* Copyright 2009 Vuze, Inc.  All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/


package com.aelitis.azureus.core.devices.impl;

import java.util.*;
import java.io.IOException;
import java.net.*;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.AESemaphore;
import org.gudy.azureus2.core3.util.AEThread2;
import org.gudy.azureus2.core3.util.Base32;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.DelayedEvent;
import org.gudy.azureus2.core3.util.RandomUtils;
import org.gudy.azureus2.core3.util.SimpleTimer;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.TimerEvent;
import org.gudy.azureus2.core3.util.TimerEventPerformer;
import org.gudy.azureus2.core3.util.TimerEventPeriodic;
import org.gudy.azureus2.platform.PlatformManagerFactory;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.tracker.Tracker;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebContext;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageGenerator;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageRequest;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageResponse;
import org.gudy.azureus2.pluginsimpl.local.PluginInitializer;

import com.aelitis.azureus.core.devices.Device;
import com.aelitis.azureus.core.devices.DeviceManagerException;
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdmin;


public class
DeviceTivoManager
{
  private static final String    LF        = "\n";
  private static final int    CONTROL_PORT  = 2190;
 
  private DeviceManagerImpl    device_manager;
  private PluginInterface     plugin_interface;
 
  private boolean  is_enabled;
  private String  uid;
  private String  server_name  = "Vuze";
 
  private Searcher  current_search;
 
  private volatile boolean  manager_destroyed;
 
  protected
  DeviceTivoManager(
    DeviceManagerImpl    _dm )
  {
    device_manager = _dm;
  }
 
  protected void
  startUp()
  {
    plugin_interface = PluginInitializer.getDefaultInterface();

    if ( COConfigurationManager.getStringParameter("ui").equals("az2")){
     
      is_enabled = false;
     
    }else{
   
      is_enabled = COConfigurationManager.getBooleanParameter( "devices.tivo.enabled", true );
    }
   
    uid = COConfigurationManager.getStringParameter( "devices.tivo.uid", null );
   
    if ( uid == null ){
     
      byte[] bytes = new byte[8];
     
      RandomUtils.nextBytes( bytes );
     
      uid = Base32.encode( bytes );
     
      COConfigurationManager.setParameter( "devices.tivo.uid", uid );
    }
       
      // set up default server name
   
    try{
      String cn = PlatformManagerFactory.getPlatformManager().getComputerName();
     
      if ( cn != null && cn.length() > 0 ){
     
        server_name += " on " + cn;
      }
    }catch( Throwable e ){
    }

      // try to use the media server's name
   
    try{
      server_name = (String)device_manager.getUPnPManager().getUPnPAVIPC().invoke( "getServiceName", new Object[]{});
             
    }catch( Throwable e ){
    }
   
    boolean  found_tivo = false;
   
    for ( Device device: device_manager.getDevices()){
     
      if ( device instanceof DeviceTivo ){
       
        found_tivo = true;
       
        break;
      }
    }
     
    if ( found_tivo || device_manager.getAutoSearch()){
     
      search( found_tivo, false );
    }
  }
 
  protected boolean
  isEnabled()
  {
    return( is_enabled );
  }
 
  protected void
  setEnabled(
    boolean  enabled )
  {
    COConfigurationManager.setParameter( "devices.tivo.enabled", enabled );
   
    if ( enabled ){
     
      search( false, true );
     
    }else{
     
      for ( Device device: device_manager.getDevices()){
       
        if ( device instanceof DeviceTivo ){
   
          device.remove();
        }
      }
    }
  }
 
  protected void
  search()
  {
    search( false, false );
  }
 
  protected void
  search(
    boolean    persistent,
    boolean    async )
  {
    try{
      synchronized( this ){
       
        if ( current_search == null ){
         
          current_search = new Searcher( persistent, async );
         
        }else{
         
          if ( !current_search.wakeup()){
           
            current_search = new Searcher( persistent, async );
          }
        }
      }
    }catch( Throwable e ){
     
      Debug.out( e );
    }
  }
 
  protected byte[]
  encodeBeacon(
    boolean    is_broadcast,
    int      my_port )
 
    throws IOException
  {
    String beacon =
      "tivoconnect=1" + LF +
      "swversion=1" + LF + 
      "method=" + (is_broadcast?"broadcast":"connected") + LF +
      "identity=" + uid + LF +
      "machine=" + server_name + LF +
      "platform=pc" + LF +
      "services=TiVoMediaServer:" + my_port + "/http";

    return( beacon.getBytes( "ISO-8859-1" ));
  }
 
  protected Map<String,String>
  decodeBeacon(
    byte[]    buffer,
    int      length )
   
    throws IOException
  {
    String str = new String( buffer, 0, length, "ISO-8859-1" );
   
    String[]  lines = str.split( LF );
   
    Map<String,String>  map = new HashMap<String, String>();
   
    for (String line:lines ){
     
      int pos = line.indexOf( '=' );
     
      if ( pos > 0 ){
       
        map.put( line.substring( 0, pos ).trim().toLowerCase(), line.substring( pos+ 1 ).trim());
      }
    }
   
    return( map );
  }
 
  protected boolean
  receiveBeacon(
    InetAddress  sender,
    byte[]    buffer,
    int      length )
  {
    if ( is_enabled ){
     
      try{
        Map<String,String>  map = decodeBeacon( buffer, length );
       
        String id = map.get( "identity" );
       
        if ( id == null || id.equals( uid )){
         
          return( false );
        }
       
        String platform = map.get( "platform" );
       
        if ( platform != null && platform.toLowerCase().startsWith( "tcd/")){
         
          String classification = "tivo." + platform.substring( 4 ).toLowerCase();
         
          foundTiVo( sender, id, classification, (String)map.get( "machine" ));
         
          return( true );
        }
      }catch( Throwable e ){
       
        log( "Failed to decode beacon", e );
      }
    }
   
    return( false );
  }
 
  protected DeviceTivo
  foundTiVo(
    InetAddress    address,
    String      uid,
    String      classification,
    String      machine )
  {
    uid  = "tivo:" + uid;
   
    DeviceImpl[] devices = device_manager.getDevices();
       
    for ( DeviceImpl device: devices ){
     
      if ( device instanceof DeviceTivo ){
       
        DeviceTivo tivo = (DeviceTivo)device;
       
        if ( device.getID().equals( uid )){
       
          if ( classification != null ){
           
            String existing_classification = device.getClassification();
           
            if ( !classification.equals( existing_classification )){
             
              device.setPersistentStringProperty( DeviceImpl.PP_REND_CLASSIFICATION,classification );
            }
          }
         
          tivo.found( this, address, server_name, machine );
         
          return( tivo );
        }
      }
    }
   
      // unfortunately we can't deduce the series from the browse request so start off with a series 3
      // this will be corrected later if we receive a beacon which *does* contain the series details
   
    if ( classification == null ){
     
      classification = "tivo.series3";
    }
   
    DeviceTivo new_device = new DeviceTivo( device_manager, uid, classification );
   
    DeviceTivo result = (DeviceTivo)device_manager.addDevice( new_device );
   
      // possible race here so handle case where device already present
   
    if ( result == new_device ){
   
      new_device.found( this, address, server_name, machine );
    }
   
    return( result );
  }
 
  protected void
  log(
    String    str )
  {
    if ( device_manager == null ){
     
      System.out.println( str );
     
    }else{
     
      device_manager.log( "TiVo: " + str );
    }
  }
 
  protected void
  log(
    String    str,
    Throwable   e )
  {
    if ( device_manager == null ){
     
      System.out.println( str );
     
      e.printStackTrace();
     
    }else{
   
      device_manager.log( "TiVo: " + str, e );
    }
  }
 
  protected class
  Searcher
  {
    private static final int  LIFE_MILLIS = 10*1000;
   
    private long start = SystemTime.getMonotonousTime();
   
    private int    tcp_port;
   
    private DatagramSocket     control_socket;
    private TrackerWebContext   twc;
    private TimerEventPeriodic  timer_event;
   
    private volatile boolean    persistent;
   
    private volatile boolean    search_destroyed;
   
    protected
    Searcher(
      boolean    _persistent,
      boolean    _async )
   
      throws DeviceManagerException
    {
      try{
        int  last_port = COConfigurationManager.getIntParameter( "devices.tivo.net.tcp.port", 0 );
       
        if ( last_port > 0 ){
         
          try{
            ServerSocket ss = new ServerSocket( last_port );
         
            ss.setReuseAddress( true );
           
            ss.close();
           
          }catch( Throwable e ){
           
            last_port = 0;
          }
        }
       
        twc = plugin_interface.getTracker().createWebContext( last_port, Tracker.PR_HTTP );
       
        tcp_port = twc.getURLs()[0].getPort();

        COConfigurationManager.setParameter( "devices.tivo.net.tcp.port", tcp_port );
       
        twc.addPageGenerator(
          new TrackerWebPageGenerator()
          {
            public boolean
            generate(
              TrackerWebPageRequest   request,
              TrackerWebPageResponse   response )
           
              throws IOException
            {
              String  id = (String)request.getHeaders().get( "tsn" );
             
              if ( id == null ){
               
                id = (String)request.getHeaders().get( "tivo_tcd_id" )
              }
             
              if ( id != null && is_enabled ){
               
                persistent = true;
               
                DeviceTivo tivo = foundTiVo( request.getClientAddress2().getAddress(), id, null, null );
               
                return( tivo.generate( request, response ));
              }
             
              return( false );
            }
          });
               
        control_socket  = new DatagramSocket( null );
         
        control_socket.setReuseAddress( true );
         
        try{
          control_socket.setSoTimeout( 60*1000 );
         
        }catch( Throwable e ){
        }
       
        InetAddress bind = NetworkAdmin.getSingleton().getSingleHomedServiceBindAddress();
       
        control_socket.bind( new InetSocketAddress( bind, CONTROL_PORT ));
   
        timer_event =
          SimpleTimer.addPeriodicEvent(
            "Tivo:Beacon",
            60*1000,
            new TimerEventPerformer()
            {
              public void
              perform(
                TimerEvent   event )
              {
                if ( !( manager_destroyed || search_destroyed )){
               
                  sendBeacon();
                }
               
                  // see if time to auto-shutdown searching
               
                if ( !persistent ){
               
                  synchronized( DeviceTivoManager.this ){
                 
                    if ( SystemTime.getMonotonousTime() - start >= LIFE_MILLIS ){
                     
                      log( "Terminating search, no devices found" );
                     
                      current_search = null;
                     
                      destroy();
                    }
                  }
                }
              }
            })
       
        final AESemaphore start_sem = new AESemaphore( "TiVo:CtrlListener" );
       
        new AEThread2( "TiVo:CtrlListener", true )
        {
          public void
          run()
          {
            start_sem.release();
           
            long  successful_accepts   = 0;
            long  failed_accepts    = 0;
   
            while( !( manager_destroyed || search_destroyed )){
             
              try{
                byte[] buf = new byte[8192];
               
                DatagramPacket packet = new DatagramPacket(buf, buf.length );
                       
                control_socket.receive( packet );
                 
                successful_accepts++;
               
                failed_accepts   = 0;
               
                if ( receiveBeacon( packet.getAddress(), packet.getData(), packet.getLength())){
                 
                  persistent = true;
                }
               
              }catch( SocketTimeoutException e ){
                             
              }catch( Throwable e ){
               
                if ( control_socket != null && !search_destroyed && !manager_destroyed ){
               
                  failed_accepts++;
               
                  log( "UDP receive on port " + CONTROL_PORT + " failed", e );
                }
               
                if (( failed_accepts > 100 && successful_accepts == 0 ) || failed_accepts > 1000 ){
                 
                  log( "    too many failures, abandoning" );
   
                  break;
                }
              }
            }       
          }
        }.start();
         
        if ( _async ){
         
          new DelayedEvent(
            "search:delay",
            5000,
            new AERunnable()
            {
              public void
              runSupport()
              {
                sendBeacon();
              }
            })
        }else{
         
          start_sem.reserve( 5000 );
       
          sendBeacon();
        }

        log( "Initiated device search" );
       
      }catch( Throwable e ){
     
        log( "Failed to initialise search", e );

        destroy();
       
        throw( new DeviceManagerException( "Creation failed",e ));
      }
    }
   
    protected void
    sendBeacon()
    {
      if ( is_enabled ){
       
        try{
          byte[]   bytes = encodeBeacon( true, tcp_port );
         
          control_socket.send( new DatagramPacket( bytes, bytes.length, InetAddress.getByName( "255.255.255.255" ), CONTROL_PORT ));
         
        }catch( Throwable e ){
         
          log( "Failed to send beacon", e );
        }
      }
    }
   
    protected boolean
    wakeup()
    {
      synchronized( DeviceTivoManager.this ){
       
        if ( search_destroyed ){
         
          return( false );
        }
       
        start = SystemTime.getMonotonousTime();
      }
     
      sendBeacon();
     
      return( true );
    }
   
    protected void
    destroy()
    {
      search_destroyed = true;
     
      if ( twc != null ){
       
        twc.destroy();
       
        twc = null;
      }
     
      if ( timer_event != null ){
       
        timer_event.cancel();
       
        timer_event = null;
      }
     
      if ( control_socket != null ){
       
        try{
          control_socket.close();
         
        }catch( Throwable e ){
        }
       
        control_socket = null;
      }
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.devices.impl.DeviceTivoManager$Searcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.