Main.java :  » Music » harmonium » com » tivo » hme » host » sample » Java Open Source

Java Open Source » Music » harmonium 
harmonium » com » tivo » hme » host » sample » Main.java
//////////////////////////////////////////////////////////////////////
//
// File: Main.java
//
// Copyright (c) 2003-2005 TiVo Inc.
//
//////////////////////////////////////////////////////////////////////

package com.tivo.hme.host.sample;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.BindException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;

import com.tivo.hme.host.io.FastInputStream;
import com.tivo.hme.host.util.ArgumentList;
import com.tivo.hme.host.util.Config;
import com.tivo.hme.host.util.Misc;
import com.tivo.hme.interfaces.IArgumentList;
import com.tivo.hme.interfaces.IFactory;
import com.tivo.hme.interfaces.IHmeConstants;
import com.tivo.hme.interfaces.ILogger;

/**
 * Main SDK class to launch one or more factories.
 *
 * @author      Jonathan Payne
 */
@SuppressWarnings("unchecked")
public class Main implements ILogger
{
    final static int DEFAULT_PORT = 7288;
    public final static String DNSSD_KEY = "dnssd";
    
    protected Config config;
    protected Listener listener;
    protected List factories = new ArrayList();
    protected JmDNS rv[];

    public Main(ArgumentList args) throws IOException
    {
        this(args, true);
    }
    
    public Main(ArgumentList args, boolean start) throws IOException
    {
        //
        // build config
        //

        config = new Config();
        config.put("listener.debug", "" + args.getBoolean("-d"));

        int port = args.getInt("--port", DEFAULT_PORT);
        config.put("http.ports", "" + port);

        //
        // determine list of interfaces
        //

        String interfaceList = "";

        String nomdns = args.getValue("--nomdns", null);
        if (nomdns != null) {
            // turn off mdns, bind to whatever the user specified
            interfaceList += "," + nomdns;
        } else {
            String intf = args.getValue("--intf", null);
            if (intf != null) {
                do {
                    if (isIPAddress(intf)) {
                        interfaceList += "," + intf;
                    } else {
                        // network interface name?
                        NetworkInterface ni = NetworkInterface.getByName(intf);
                        if (ni == null) {
                            System.out.println("\"" + intf + "\" is not a valid ipv4, ipv6, or network interface name. The\nnetwork interfaces on this machine are:");
                            printNetworkInterfaces();
                            throw new IOException("network interface not found: " + intf);
                        }
                        for (Enumeration e = ni.getInetAddresses() ; e.hasMoreElements() ; ) {
                            InetAddress addr = (InetAddress)e.nextElement();
                            interfaceList += "," + addr.getHostAddress();
                        }
                    }
                    intf = args.getValue("--intf", null);
                } while (intf != null);
            } else {
                // add at most one regular, and one linklocal interface
                boolean regularIntf = false;
                boolean linklocalIntf = false;
                
                InetAddress addrs[] = Misc.getInterfaces();
                for (int i = 0 ; i < addrs.length ; i++) {
                    InetAddress addr = addrs[i];
                    String str = addr.getHostAddress();
                    if (str.equals("127.0.0.1")) {
                        continue;
                    }
                    if (str.startsWith("169.254.")) {
                        if (!linklocalIntf) {
                            linklocalIntf = true;
                            interfaceList += "," + str;
                        }
                    } else if (!regularIntf) {
                        regularIntf = true;
                        interfaceList += "," + str;
                    }
                }
            }
        }
        if ("true".equals(System.getProperty("hme.loopback"))) {
            interfaceList += "," + "127.0.0.1";
        }
        config.put("http.interfaces", interfaceList);

        //
        // determine list of factories
        //

        String launcher = args.getValue("--launcher", null);
        String jardir = args.getValue("--jars", null);
        String jarfile = args.getValue("--jar", null);
        try {
            // load the factories
            if (launcher != null) {
                loadLaunchFile(launcher);
            } else if (jardir != null) {
                loadJarFiles(jardir);
            } else if (jarfile != null) {
                loadJarFile(new File(jarfile));
            } else if (start) {
                createFactory(args, ClassLoader.getSystemClassLoader());
            }

            // bail if we didn't get any
            if (start && factories.size() == 0) {
                System.out.println("Failed to instantiate any HME apps");
                return;
            }

            //
            // start the listener
            //
            
            try {
                listener = new Listener(config, this);
                Listener.DEBUG_FLUSHES = true;
            } catch (BindException e) {
                if (port == DEFAULT_PORT) {
                    // hm - default port failed, try a random one
                    config.put("http.ports", "0");
                    listener = new Listener(config, this);                
                } else {
                    throw e;
                }
            }
        
            //
            // get ready for JmDNS
            //
            
            if (nomdns == null) {
                String interfaces[] = listener.getInterfaces();
                rv = new JmDNS[interfaces.length];
                for (int i = 0; i < interfaces.length; ++i) {
                    rv[i] = new JmDNS(InetAddress.getByName(interfaces[i]));
                }
            }
            
            //
            // now start the factories
            //
            
            listener.setFactories(factories);
            for (Iterator i = factories.iterator(); i.hasNext(); ) {
                IFactory factory = (IFactory)i.next();
                register(factory);
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("error: " + e.getMessage());
            usage();
        }
    }

    private void usage()
    {
        System.out.println("usage: Main [options] class");
        System.out.println();
        System.out.println("Options:");
        System.out.println(" --port <port>         listen on a specific port");
        System.out.println(" --intf <interface>    listen on a specific interface");
        System.out.println(" --nomdns <interface>  listen on a specific interface, without mdns");
        System.out.println(" --launcher <file>     start factories listed in file");
        System.out.println(" --jars <dir>          scan directory for HME app jar files");
        System.out.println(" --jar <jarfile>       start factory for the given jar");
        System.exit(1);
    }

    //
    // helpers for building list of interfaces
    //

    static boolean isIPAddress(String s)
    {
        return isIPv4Address(s) || isIPv6Address(s);
    }

    static boolean isIPv4Address(String s)
    {
        int count = 0;
        for (StringTokenizer tokens = new StringTokenizer(s, ".") ; tokens.hasMoreTokens() ; ) {
            try {
                Integer.parseInt(tokens.nextToken());
            } catch (NumberFormatException e) {
                return false;
            }
            ++count;
        }
        return (count == 4);
    }

    // REMIND : this isn't 100% accurate
    static boolean isIPv6Address(String s)
    {
        if (s.indexOf(':') == -1) {
            return false;
        }
        for (StringTokenizer tokens = new StringTokenizer(s, ":") ; tokens.hasMoreTokens() ; ) {
            try {
                Integer.parseInt(tokens.nextToken(), 16);
            } catch (NumberFormatException e) {
                return false;
            }
        }
        return true;
    }

    void printNetworkInterfaces() throws IOException
    {
        //
        // this only works in JDK 1.4
        //
        for (Enumeration e = NetworkInterface.getNetworkInterfaces() ; e.hasMoreElements() ; ) {
            NetworkInterface ni = (NetworkInterface)e.nextElement();
            System.out.print("  " + ni.getName());
            for (Enumeration e2 = ni.getInetAddresses() ; e2.hasMoreElements() ; ) {
                InetAddress addr = (InetAddress)e2.nextElement();
                System.out.print(" " + addr.getHostAddress());
            }
            System.out.println();
        }
    }

    //
    // jar file loading
    //

    private StringBuffer addArg(StringBuffer buf, String name, String value)
    {
        if (value != null) {
            if (name != null) {
                buf.append(' ').append(name);
            }
            buf.append(' ').append(value);
        }
        return buf;
    }

    /**
     * Scans a directory for jar files and tries to instantiate each as a HME
     * app.  Jar files must contain the following attributes:
     *
     * HME-Class:       the name of the HME app class
     * HME-Arguments:   arguments to be passed to the app factory's init method
     *
     * @see loadJarFile(File)
     * @param jardir full path to the jars directory.
     */
    private void loadJarFiles(String jardir) throws IOException
    {
        File dir = new File(jardir);
        String list[] = dir.list();
        if (list == null) {
            return;
        }
        for (int i = 0; i < list.length; i++) {
            if (!list[i].endsWith(".jar")) {
                continue;
            }
            File jarfile = new File(dir, list[i]);
            loadJarFile(jarfile);
        }
    }

    /**
     * Tries to instantiate an HME app for the given jar file.  The jar file
     * must contain the following attributes:
     *
     * HME-Class:       the name of the HME app class
     * HME-Arguments:   arguments to be passed to the app factory's init method
     *
     * Bad jar files are ignored, and warnings are logged.
     *
     * @see loadJarFiles(String)
     * @param jarFile a jar file
     */
    private void loadJarFile(File jarFile) throws IOException
    {
        try {
            JarFile jf = new JarFile(jarFile, true);
            Manifest manifest = jf.getManifest();
            Attributes attrs = manifest.getMainAttributes();
            StringBuffer args = new StringBuffer(64);

            // check for factory, class and arguments
            addArg(args, "--class", attrs.getValue("HME-Class"));
            addArg(args, null, attrs.getValue("HME-Arguments"));
            createFactory(new ArgumentList(args.toString()),
                          new JarClassLoader(jf, this, null));
        } catch (Exception e) {
            log(ILogger.LOG_WARNING, "Ignoring jar file: " + jarFile);
            log(ILogger.LOG_WARNING, "Exception occurred: " + e);
            if (Listener.DEBUG) {
                e.printStackTrace();
            }
        }
    }        
        
    /**
     * Scans a text file which lists HME application class names and arguments,
     * and creates factories for them.
     *
     * @param file full path to the launch file
     */
    public void loadLaunchFile(String file) throws IOException
    {
        FastInputStream fin = new FastInputStream(new FileInputStream(file), 1024);
        LineNumberReader in = new LineNumberReader(new InputStreamReader(fin));
        try {
            String ln = in.readLine();
            while (ln != null) {
                ln = ln.trim();
                if (!ln.startsWith("#") && ln.length() > 0) {
                    createFactory(new ArgumentList(ln),
                                  ClassLoader.getSystemClassLoader());
                }
                ln = in.readLine();
            }
        } finally {
            in.close();
        }
    }

    /**
     * Create a factory with the specified arguments and class loader.
     */
    private void createFactory(ArgumentList args, ClassLoader loader)
    {
        try {
            String classname = args.getValue("--class", null);
            if (classname == null) {
                classname = args.shift();
            }
            Class appClass = Class.forName(classname, true, loader);
            Class[] paramTypes = {String.class, ClassLoader.class, IArgumentList.class};
            Method getFactoryMethod = appClass.getMethod("getAppFactory", paramTypes);
                        
            Object[] params = {classname, loader, args };
    
            IFactory factory = (IFactory)getFactoryMethod.invoke(null, params);
            
            args.checkForIllegalFlags();
            factories.add(factory);
        } catch (ClassNotFoundException e) {
            System.out.println("error: class not found: " + e.getMessage());
            System.out.println("error: check the classpath and access permissions");
        } catch (IllegalAccessException e) {
            System.out.println("error: illegal access: " + e.getMessage());
            System.out.println("error: make sure the class is public and has a public default constructor");
        } catch (NoSuchMethodException e) {
            System.out.println("error: no constructor: " + e.getMessage());
            System.out.println("error: make sure the class is public and has a public default constructor");
        } catch (IllegalArgumentException e) {
            System.out.println("error: illegal argument: " + e.getMessage());
            System.out.println("error: make sure the class is public and has a public static getAppFactory method with correct parameters");
        } catch (InvocationTargetException e) {
            System.out.println("error: unable to invoke method: " + e.getMessage());
            System.out.println("error: make sure the class is public and has a public static getAppFactory method");
        }
    }

    /**
     * Register a factory if MDNS is turned on.
     */
    protected void register(IFactory factory) throws IOException
    {
        String interfaces[] = listener.getInterfaces();
        int ports[] = listener.getPorts();
        for (int i = 0; i < interfaces.length; ++i) {
            for (int j = 0; j < ports.length; ++j) {
                String url = ("http://" + interfaces[i] + ":" +
                              ports[j] + factory.getAppName());

                if (rv == null) {
                    System.out.println(url + " [no mdns]");
                    continue;
                }

                System.out.println("MDNS: " + url);

                //
                // attempt to register using native mDNS daemon
                //
                DNSSDRequest dnssd = null;
                
                try {
                    dnssd = new DNSSDRequest();
                } catch (IOException e) {
                    // dnssd is not present, but it is OK
                }
                
                if (dnssd != null) {
                    try {
                        dnssd.registerService(factory.getAppTitle(), IHmeConstants.MDNS_DNSSD_TYPE, url);
                        factory.getFactoryData().put(DNSSD_KEY, dnssd);
                        continue;
                        
                    } catch (IOException e) {
                        // DNSSd Failed so make sure it is cleaned up
                        dnssd.close();
                        dnssd = null;
                    }
                }
                //
                // register using jmdns
                //
                if (dnssd == null) {
                  rv[j].registerService(getServiceInfo(IHmeConstants.MDNS_TYPE, factory, ports[j]));
                }
            }
        }
    }

    protected void unregister(IFactory factory) throws IOException
    {
        String interfaces[] = listener.getInterfaces();
        int ports[] = listener.getPorts();
        for (int i = 0; i < interfaces.length; ++i) {
            for (int j = 0; j < ports.length; ++j) {
                String url = ("http://" + interfaces[i] + ":" +
                              ports[j] + factory.getAppName());

                if (rv == null) {
                    System.out.println(url + " [no mdns]");
                    continue;
                }

                System.out.println("MDNS REMOVE: " + url);

                //
                // attempt to unregister using native mDNS daemon
                //
                DNSSDRequest dnssd = (DNSSDRequest) factory.getFactoryData().get(DNSSD_KEY);
                if ( dnssd != null) {
                    //
                    // attempt to unregister using native mDNS daemon
                    //
                    dnssd.close();
                    dnssd = null;
                } else {
                    //
                    // unregister using jmdns
                    //
                    rv[j].unregisterService(getServiceInfo(IHmeConstants.MDNS_TYPE, factory, ports[j]));
                }
            }
        }
    }

    protected ServiceInfo getServiceInfo(String mdns_type, IFactory factory, int port)
    {
        Hashtable atts = new Hashtable();
        atts.put("path", factory.getAppName());
        atts.put("version", (String)factory.getFactoryData().get(IFactory.HME_VERSION_TAG));
        return new ServiceInfo(mdns_type, factory.getAppTitle() + "." + mdns_type, port, 0, 0, atts);
    }

    /**
     * @return Returns the factories.
     */
    public List getFactories() {
        return factories;
    }
    /**
     * @return Returns the listener.
     */
    public Listener getListener() {
        return listener;
    }

    //
    // from IMain
    //

    public void log(int priority, String s)
    {
        System.out.println("LOG: " + s);        
    }

    public static void main(String argv[]) throws IOException
    {
        // print the version of the SDK every time at startup
        new HostingVersion().printVersion(System.out);
        new Main(new ArgumentList(argv));
    }
}
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.