homenetapp.HomeNetApp.java Source code

Java tutorial

Introduction

Here is the source code for homenetapp.HomeNetApp.java

Source

/*
 * Copyright (c) 2011 Matthew Doll <mdoll at homenet.me>.
 *
 * This file is part of HomeNet.
 *
 * HomeNet 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * HomeNet 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 HomeNet.  If not, see <http://www.gnu.org/licenses/>.
 */
package homenetapp;

import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.text.*;

import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;
import java.util.zip.*;
import java.lang.Throwable;
import homenet.*;

//import org.apache.xmlrpc.XmlRpcClient;

import org.apache.commons.configuration.*;

/**
 *
 * @author mdoll
 */
public class HomeNetApp {

    public HashMap<Integer, String[]> commands;
    public PropertiesConfiguration config = null;
    public homenet.Stack homenet;
    public SerialManager serialmanager;
    private XmlrpcClient _xmlrpcClient = null;
    private XmlrpcServer _xmlrpcServer = null;
    private UPnP upnp = new UPnP();
    //core settings

    final public String version = "0.0.1";

    public int nodeId = 0xff;

    public String clientServer = "homenet.me";
    public String clientApiKey = "";

    public boolean serverEnabled = true;
    public boolean serverUpnpEnabled = true;
    public int serverPort = 2443;
    public boolean configDone = false;

    List<String> portsSerial = new ArrayList();

    // public HashMap Por;
    public HomeNetApp() {
        loadConfig();
        loadCommands();

        homenet = new homenet.Stack(0xff);
        homenet.init();
    }

    public void start() throws Exception {

        // HashMap<String,Port> ports = new HashMap<String,Port>();
        //  HashMap<Integer,Device> devices = new HashMap<Integer,Device>();
        serialmanager = new SerialManager(homenet);

        //start client
        startClient();

        //start server
        if (serverEnabled == true) {

            startServer();

            if (serverUpnpEnabled == true) {
                startUpnp();
            }
        }

    }

    public void startServer() throws Exception {
        System.out.println("Starting XML RPC Server on port " + serverPort);
        _xmlrpcServer = new XmlrpcServer(serverPort);
        XmlrpcCalls calls = new XmlrpcCalls(this);
        _xmlrpcServer.add("HomeNet", calls);

    }

    public void stopServer() {
        System.out.println("Stopping XML RPC Server on port " + serverPort);
        if (_xmlrpcServer != null) {
            _xmlrpcServer.stop();
            _xmlrpcServer = null;
        }
    }

    public void startClient() throws Exception {
        System.out.println("Starting XML RPC Client to " + clientServer);
        System.out.println("    with Api Key: " + clientApiKey);
        _xmlrpcClient = new XmlrpcClient(clientServer, clientApiKey);

        boolean reply = (Boolean) _xmlrpcClient.execute("homenet.apikey.validate", clientApiKey);

        if (reply == false) {
            throw new Exception("Invalid API Key");
        }

        // boolean loadXmlrpc = true;
        homenet.addPort("xmlrpc", new PortXmlrpc(homenet, _xmlrpcClient));

    }

    public void stopClient() {
        System.out.println("Stopping XML RPC Client");
        homenet.removePort("xmlrpc");
        _xmlrpcClient = null;
    }

    public void startUpnp() {
        System.out.println("Starting UPnP port forwarding for port " + serverPort);
        Thread runUpnp = new Thread() {
            public void run() {
                upnp = new UPnP();
                upnp.forwardPort(serverPort);
            }
        };
        runUpnp.start();
    }

    public void stopUpnp() {
        System.out.println("Stopping UPnP port forwarding for port " + upnp.port);
        upnp.exit();
    }

    private boolean loadConfig() {
        try {
            config = new PropertiesConfiguration("homenet.properties.txt");

            if (config.getString("client.server") != null) {
                clientServer = config.getString("client.server");
            }

            //core settings

            clientApiKey = config.getString("client.apikey");
            serverEnabled = config.getBoolean("server.enabled");
            serverUpnpEnabled = config.getBoolean("server.upnp");
            serverPort = config.getInt("server.port");

            configDone = config.getBoolean("config.done");

            portsSerial = config.getList("ports.serial");
        } catch (Exception e) {
            config = new PropertiesConfiguration();
            return false;
        }
        return true;
    }

    public void saveConfig() throws Exception {
        System.out.println("Saving Config");
        config.setProperty("client.server", clientServer);
        config.setProperty("client.apikey", clientApiKey);
        config.setProperty("server.enabled", serverEnabled);
        config.setProperty("server.upnp", serverUpnpEnabled);
        config.setProperty("server.port", serverPort);
        config.setProperty("config.done", configDone);

        config.save("homenet.properties.txt");
    }

    private void loadCommands() {

        getAppPath("commands.txt");

        commands = new HashMap<Integer, String[]>();

        String[] strings = loadStrings("commands.txt");

        for (int i = 0; i < strings.length; i++) {
            String[] r = splitTokens(strings[i], "\t");
            commands.put(Integer.parseInt(r[0], 16), r);
        }

    }

    class compareCommands implements Comparator {

        public int compare(Object i1, Object i2) {
            return ((Integer) i1).intValue() - ((Integer) i2).intValue();
        }
    }

    public Object[] getCommandKeys() {

        //System.out.println(commands.keySet().size());
        // System.out.println("Size: "+commands.size());

        Object[] rows = commands.keySet().toArray();
        Arrays.sort(rows, new compareCommands());
        System.out.println("Commands Loaded: " + rows.length);

        //Object[] rows = new Object[1]; 
        // Integer[] test =  {new Integer(2),new Integer(2),new Integer(2),new Integer(2)};

        // System.exit(-1);
        return rows;

    }

    public String getAppPath(String filename) {
        //@todo find the right path
        String path = HomeNetApp.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        path = path.replaceAll("%20", " ");
        path = path.substring(0, path.lastIndexOf("/"));

        System.err.println(path + "/" + filename);
        return path + "/" + filename;
        //return "C:\\Users\\mdoll\\Documents\\NetBeansProjects\\HomeNet.me-App\\" + filename;
        // return "C:\\Projects (Safe)\\HomeNet.me-App\\" + filename;
    }

    public void exit() {
        serialmanager.exit();
        try {
            saveConfig();
        } catch (Exception e) {
        }

    }

    class SerialManager {

        public List<String> portList = new CopyOnWriteArrayList();
        public List<String> selectedPorts = new CopyOnWriteArrayList();
        private SerialCheckThread sThread;
        private homenet.Stack _homeNet;
        private List<SerialListener> _listeners = new CopyOnWriteArrayList();

        public SerialManager(homenet.Stack stack) {

            _homeNet = stack;

        }

        public void start() throws Exception {
            //loadSerialPorts();
            checkSerialPorts();

            // List<String> list = config.getList("ports.serial");
            for (String s : portsSerial) {
                serialmanager.activatePort(s);
            }

            sThread = new SerialCheckThread(1000);
            sThread.setPriority(3);
            sThread.start();
        }

        public void exit() {
            config.setProperty("ports.serial", selectedPorts);
        }

        public void addListener(SerialListener l) {
            _listeners.add(l);
        }

        public boolean activatePort(String port) throws Exception {
            System.out.println("Activate Port: " + port);
            if (!portList.contains(port)) {
                return false;
            }

            //@throws Exception
            _homeNet.addPort(port, new PortSerial(_homeNet, new Serial(port)));

            if (!selectedPorts.contains(port)) {
                selectedPorts.add(port);
            }

            for (SerialListener l : _listeners) {
                l.portActivated(port);
            }
            return true;
        }

        public void deactivatePort(String port) {
            deactivatePort(port, true);
        }

        public void deactivatePort(String port, boolean remove) {
            System.out.println("Deactivate Port: " + port);
            //  if (currentPorts.containsKey(port)) {
            _homeNet.removePort(port);
            //     }
            if (remove == true) {
                selectedPorts.remove(port);
            }

            for (SerialListener l : _listeners) {
                l.portDeactivated(port);
            }
        }

        public void checkSerialPorts() {

            // System.out.println("HomeNetApp.checkSerialPorts");
            List<String> currentList = Serial.listPorts();
            Map currentPorts = _homeNet.getPorts();

            //check for new ports
            for (String k : currentList) {
                if (!portList.contains(k)) {
                    portList.add(k);
                    System.out.println("New port found: " + k);

                    //check to see if this one is on our favorites list
                    if (selectedPorts.contains(k)) {
                        final String l = k;

                        System.out.println(
                                "Hey! port " + l + " is on the VIP list, will try and add it in 10 seconds");
                        //brand new serial ports are grumpy. this will delay adding it for 10 seconds
                        Thread delay = new Thread() {
                            public void run() {
                                try {
                                    Thread.sleep(10 * 1000);
                                    activatePort(l);
                                } catch (Exception e) {
                                }

                            }
                        };
                        delay.start();
                    }

                    //    System.out.println("List size: " + _listeners.size());
                    //process Listeners
                    for (SerialListener l : _listeners) {
                        l.portAdded(k);
                    }
                }
            }

            //check for removed ports 
            //loops throught the old list and compares it to the new one

            for (String k : portList) {
                if (!currentList.contains(k)) {
                    //remove from portlist too;
                    System.out.println("Port " + k + " was disconnected");
                    System.out.println("Auto remove " + k);

                    deactivatePort(k, false);
                    currentPorts.remove(k);

                    portList.remove(k);

                    //process Listeners
                    for (SerialListener l : _listeners) {
                        l.portRemoved(k);
                    }

                }
            }
        }

        public void loadSerialPorts() {
        }

        void saveSerialPorts() {
            config.setProperty("test", "test");
        }

        //based on SimpleThread
        class SerialCheckThread extends Thread {

            boolean running; // Is the thread running?  Yes or no?
            int wait; // How many milliseconds should we wait in between executions?
            String id; // Thread name
            int count; // counter
            boolean check;

            // Constructor, create the thread
            // It is not running by default
            SerialCheckThread(int w) {
                wait = w;
                running = false;
                check = true;
            }

            // Overriding "start()"
            public void start() {
                // Set running equal to true
                running = true;

                check = true;
                // Print messages
                System.out.println(
                        "Starting Serial Port Check Thread (will execute every " + wait + " milliseconds.)");
                // Do whatever start does in Thread, don't forget this!
                super.start();
            }

            void startChecking() {
                check = true;
            }

            void stopChecking() {
                check = false;
            }

            // We must implement run, this gets triggered by start()
            public void run() {
                while (running) {
                    if (check == true) {
                        checkSerialPorts();
                    }
                    // Ok, let's wait for however long we should wait
                    try {
                        Thread.sleep((long) (wait));
                    } catch (Exception e) {
                    }
                }
                System.out.println(id + " thread is done!"); // The thread is done when we get to the end of run()
            }

            // Our method that quits the thread
            public void quit() {
                System.out.println("Quitting.");
                running = false; // Setting running to false ends the loop in run()
                // IUn case the thread is waiting. . .
                interrupt();
            }
        }
    }

    //helper functions based on functions from processing.org
    static final String WHITESPACE = " \t\n\r\f\u00A0";

    /**
     * Call openStream() without automatic gzip decompression.
     */
    public InputStream createInputRaw(String filename) {
        InputStream stream = null;

        if (filename == null) {
            return null;
        }

        if (filename.length() == 0) {
            // an error will be called by the parent function
            //System.err.println("The filename passed to openStream() was empty.");
            return null;
        }

        // safe to check for this as a url first. this will prevent online
        // access logs from being spammed with GET /sketchfolder/http://blahblah
        //        if (filename.indexOf(":") != -1) {  // at least smells like URL
        //            try {
        //                URL url = new URL(filename);
        //                stream = url.openStream();
        //                return stream;
        //
        //            } catch (MalformedURLException mfue) {
        //                // not a url, that's fine
        //            } catch (FileNotFoundException fnfe) {
        //                // Java 1.5 likes to throw this when URL not available. (fix for 0119)
        //                // http://dev.processing.org/bugs/show_bug.cgi?id=403
        //            } catch (IOException e) {
        //                // changed for 0117, shouldn't be throwing exception
        //                e.printStackTrace();
        //                //System.err.println("Error downloading from URL " + filename);
        //                return null;
        //                //throw new RuntimeException("Error downloading from URL " + filename);
        //            }
        //        }
        //
        //        // Moved this earlier than the getResourceAsStream() checks, because
        //        // calling getResourceAsStream() on a directory lists its contents.
        //        // http://dev.processing.org/bugs/show_bug.cgi?id=716
        //        try {
        //            // First see if it's in a data folder. This may fail by throwing
        //            // a SecurityException. If so, this whole block will be skipped.
        //            System.err.println("Load file");
        //            File file = new File(getAppPath(filename));
        //
        //            if (file.isDirectory()) {
        //                return null;
        //            }
        //            if (file.exists()) {
        //                try {
        //                    // handle case sensitivity check
        //                    
        //                    String filePath = file.getCanonicalPath();
        //                    System.err.println("file path: Load file"+filePath);
        //
        //                    String filenameActual = new File(filePath).getName();
        //                    // make sure there isn't a subfolder prepended to the name
        //                    String filenameShort = new File(filename).getName();
        //                    // if the actual filename is the same, but capitalized
        //                    // differently, warn the user.
        //                    //if (filenameActual.equalsIgnoreCase(filenameShort) &&
        //                    //!filenameActual.equals(filenameShort)) {
        //                    if (!filenameActual.equals(filenameShort)) {
        //                        throw new RuntimeException("This file is named "
        //                                + filenameActual + " not "
        //                                + filename + ". Rename the file "
        //                                + "or change your code.");
        //                    }
        //                } catch (IOException e) {
        //                }
        //            }
        //
        //            // if this file is ok, may as well just load it
        //            stream = new FileInputStream(file);
        //            if (stream != null) {
        //                return stream;
        //            }
        //
        //            // have to break these out because a general Exception might
        //            // catch the RuntimeException being thrown above
        //        } catch (IOException ioe) {
        //            System.err.println("File IO Error");
        //            ioe.printStackTrace();
        //        } catch (SecurityException se) {
        //            System.err.println("File Security Error");
        //        }

        // Using getClassLoader() prevents java from converting dots
        // to slashes or requiring a slash at the beginning.
        // (a slash as a prefix means that it'll load from the root of
        // the jar, rather than trying to dig into the package location)
        ClassLoader cl = getClass().getClassLoader();
        //
        //        // by default, data files are exported to the root path of the jar.
        //        // (not the data folder) so check there first.
        //        stream = cl.getResourceAsStream(filename);
        //        if (stream != null) {
        //            String cn = stream.getClass().getName();
        //            // this is an irritation of sun's java plug-in, which will return
        //            // a non-null stream for an object that doesn't exist. like all good
        //            // things, this is probably introduced in java 1.5. awesome!
        //            // http://dev.processing.org/bugs/show_bug.cgi?id=359
        //            if (!cn.equals("sun.plugin.cache.EmptyInputStream")) {
        //                return stream;
        //            }
        //        }

        // When used with an online script, also need to check without the
        // data folder, in case it's not in a subfolder called 'data'.
        // http://dev.processing.org/bugs/show_bug.cgi?id=389
        stream = cl.getResourceAsStream(filename);
        if (stream != null) {
            String cn = stream.getClass().getName();
            if (!cn.equals("sun.plugin.cache.EmptyInputStream")) {
                return stream;
            }
        }

        try {
            // attempt to load from a local file, used when running as
            // an application, or as a signed applet
            try { // first try to catch any security exceptions
                try {
                    stream = new FileInputStream(getAppPath(filename));
                    if (stream != null) {
                        return stream;
                    }
                } catch (IOException e2) {
                }

                try {
                    stream = new FileInputStream(filename);
                    if (stream != null) {
                        return stream;
                    }
                } catch (IOException e1) {
                }

            } catch (SecurityException se) {
            } // online, whups

        } catch (Exception e) {
            //die(e.getMessage(), e);
            e.printStackTrace();
        }

        return null;
    }

    public InputStream createInput(String filename) {
        InputStream input = createInputRaw(filename);
        if ((input != null) && filename.toLowerCase().endsWith(".gz")) {
            try {
                return new GZIPInputStream(input);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
        return input;
    }

    static public InputStream createInput(File file) {
        if (file == null) {
            throw new IllegalArgumentException("File passed to createInput() was null");
        }
        try {
            InputStream input = new FileInputStream(file);
            if (file.getName().toLowerCase().endsWith(".gz")) {
                return new GZIPInputStream(input);
            }
            return input;

        } catch (IOException e) {
            System.err.println("Could not createInput() for " + file);
            e.printStackTrace();
            return null;
        }
    }

    public String[] loadStrings(String filename) {
        InputStream is = createInput(filename);
        if (is != null) {
            return loadStrings(is);
        }

        System.err.println("The file \"" + filename + "\" " + "is missing or inaccessible, make sure "
                + "the URL is valid or that the file has been " + "added to your sketch and is readable.");
        return null;
    }

    static public String[] loadStrings(InputStream input) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));

            String lines[] = new String[100];
            int lineCount = 0;
            String line = null;
            while ((line = reader.readLine()) != null) {
                if (lineCount == lines.length) {
                    String temp[] = new String[lineCount << 1];
                    System.arraycopy(lines, 0, temp, 0, lineCount);
                    lines = temp;
                }
                lines[lineCount++] = line;
            }
            reader.close();

            if (lineCount == lines.length) {
                return lines;
            }

            // resize array to appropriate amount for these lines
            String output[] = new String[lineCount];
            System.arraycopy(lines, 0, output, 0, lineCount);
            return output;

        } catch (IOException e) {
            e.printStackTrace();
            //throw new RuntimeException("Error inside loadStrings()");
        }
        return null;
    }

    /**
     * Split the provided String at wherever whitespace occurs.
     * Multiple whitespace (extra spaces or tabs or whatever)
     * between items will count as a single break.
     * <P>
     * The whitespace characters are "\t\n\r\f", which are the defaults
     * for java.util.StringTokenizer, plus the unicode non-breaking space
     * character, which is found commonly on files created by or used
     * in conjunction with Mac OS X (character 160, or 0x00A0 in hex).
     * <PRE>
     * i.e. splitTokens("a b") -> { "a", "b" }
     *      splitTokens("a    b") -> { "a", "b" }
     *      splitTokens("a\tb") -> { "a", "b" }
     *      splitTokens("a \t  b  ") -> { "a", "b" }</PRE>
     */
    static public String[] splitTokens(String what) {
        return splitTokens(what, WHITESPACE);
    }

    /**
     * Splits a string into pieces, using any of the chars in the
     * String 'delim' as separator characters. For instance,
     * in addition to white space, you might want to treat commas
     * as a separator. The delimeter characters won't appear in
     * the returned String array.
     * <PRE>
     * i.e. splitTokens("a, b", " ,") -> { "a", "b" }
     * </PRE>
     * To include all the whitespace possibilities, use the variable
     * WHITESPACE, found in PConstants:
     * <PRE>
     * i.e. splitTokens("a   | b", WHITESPACE + "|");  ->  { "a", "b" }</PRE>
     */
    static public String[] splitTokens(String what, String delim) {
        StringTokenizer toker = new StringTokenizer(what, delim);
        String pieces[] = new String[toker.countTokens()];

        int index = 0;
        while (toker.hasMoreTokens()) {
            pieces[index++] = toker.nextToken();
        }
        return pieces;
    }
}