silvertrout.Network.java Source code

Java tutorial

Introduction

Here is the source code for silvertrout.Network.java

Source

/*   _______ __ __                    _______                    __
 *  |     __|__|  |.--.--.-----.----.|_     _|.----.-----.--.--.|  |_
 *  |__     |  |  ||  |  |  -__|   _|  |   |  |   _|  _  |  |  ||   _|
 *  |_______|__|__| \___/|_____|__|    |___|  |__| |_____|_____||____|
 *
 *  Copyright 2008 - Gustav Tiger, Henrik Steen and Gustav "Gussoh" Sohtell
 *
 *  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, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  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, see <http://www.gnu.org/licenses/>.
 *
 */
package silvertrout;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import java.util.Collection;

import java.util.concurrent.ConcurrentHashMap;
import java.util.TreeMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.collections4.map.AbstractHashedMap;

import silvertrout.settings.NetworkSettings;

/**
 * Network class that handles connection to the network. Sending and
 *
 *
 */
public class Network {

    private static abstract class PluginCallback {
        String callbackName;

        PluginCallback(String callbackName) {
            this.callbackName = callbackName;
        }

        abstract void callback(Plugin plugin);

        String getCallbackName() {
            return callbackName;
        }
    }

    private static class LowerCaseMap<T> extends AbstractHashedMap<String, T> {
        public LowerCaseMap() {
            super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
        }

        @Override
        protected Object convertKey(Object key) {
            if (key != null) {
                return Message.toLower(key.toString());
            }
            return AbstractHashedMap.NULL;
        }
    }

    private final NetworkSettings networkSettings;
    private final User me;
    private final Map<String, Channel> channels = new LowerCaseMap<Channel>();
    private final Map<String, Channel> unjoinedChannels = new LowerCaseMap<Channel>();
    private final Map<String, User> users = new LowerCaseMap<User>();
    private final Map<String, Plugin> plugins = new ConcurrentHashMap<String, Plugin>();
    private final IRC irc;
    private final IRCConnection connection; // do not instantiate here, let constructior decide what kind of IRCConnection we want
    private WorkerThread workerThread;
    private String motd = new String();
    /** Indicator of exception frequenzy. 0 is good. Every exception adds one and one is removed every tick() */
    private int exceptionFrequenzy = 0;
    private int tick;
    private boolean connected = false;

    /**
     * Create and connect to a new Network,
     *
     * @param irc
     * @param networkSettings the settings to use for the IRCConnection
     * @param me   - Contains settings for nickname, username, realname
     * @throws IOException if connection could not be established to network
     */
    public Network(IRC irc, NetworkSettings networkSettings, User me) throws IOException {
        this.irc = irc;
        this.networkSettings = networkSettings;
        this.me = me;

        // Load plugins:
        for (Entry<String, Map<String, String>> plugin : getIrc().getSettings()
                .getPluginsFor(networkSettings.getName()).entrySet()) {
            // settings = entry.getValue();
            if (loadPlugin(plugin.getKey())) {
                System.out.println("Plugin loaded: " + plugin.getKey());
            } else {
                System.out.println("Unable to load plugin: " + plugin.getKey());
            }
        }

        // Create worker
        createWorkerThread();

        // Connect to server
        if (networkSettings.isSecure()) {
            connection = new SecureIRCConnection(this);
        } else {
            connection = new IRCConnection(this);
        }

        for (Map.Entry<String, String> entry : getIrc().getSettings().getChannelsFor(networkSettings.getName())
                .entrySet()) {
            Channel c = new Channel(entry.getKey(), this);
            c.password = entry.getValue();
            unjoinedChannels.put(entry.getKey(), c);
        }
        System.err.println("Creating worker thread");

    }

    private void createWorkerThread() {
        if (workerThread != null && !workerThread.isStop()) {
            throw new IllegalStateException("Can only start one instance of " + "workerthread for network \""
                    + getNetworkSettings().getName() + "\"!");
        }

        workerThread = new WorkerThread();
        workerThread.setName("Network \"" + getNetworkSettings().getName() + "\"  worker thread");

        workerThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

            @Override
            public void uncaughtException(Thread t, Throwable e) {
                workerThread.cancel();
                exceptionFrequenzy++;
                System.out.println(
                        "*** Exception occured in network \"" + getNetworkSettings().getName() + "\": " + e);
                System.out.println("*** Exception frequenzy: " + exceptionFrequenzy);

                e.printStackTrace();

                if (exceptionFrequenzy >= 3) { // number greater than 1.
                    // Number found by injecting errors by messages. 3 seem
                    // to be a good number.
                    System.out.println("*** Too many exceptions! Closing " + " network \""
                            + getNetworkSettings().getName() + "\"!");
                    getConnection().forceClose();
                    return;
                }

                System.out.println("*** Creating new worker thread...");

                createWorkerThread();

                System.out.println(
                        "*** New worker thread for network \"" + getNetworkSettings().getName() + "\" started!");
            }
        });

        System.err.println(" - Starting thread");
        workerThread.start();
    }

    /**
     * Connection notifies that it is unable to fullfill its job any more.
     * Something went wrong and the connection to the IRC server failed.
     * This method is not thread safe (not an exception) and should be invoked by the network thread
     * What happens if a thread cancels itself? exception?
     */
    void onDisconnect() {
        connected = false;
        // stop worker thread
        workerThread.cancel();

        // notify plugins
        callPlugin(new PluginCallback("onDisconnected") {
            void callback(Plugin plugin) {
                plugin.onDisconnected();
            };
        });

        removeUser(me.getNickname());
        // destroy this Network instance. how? If we want to reconnect the easiest way is to create a new Network
    }

    void onNoMotd() {
        onEndOfMotd();
    }

    void onEndOfMotd() {
        onConnect();
    }

    void onMotd(String motdLine) {
        motd += motdLine + "\n";
    }

    /**
     * Someone did a +mode on user in a channel
     * @param channel
     * @param user
     * @param mode
     */
    void onGiveMode(final User giver, final Channel channel, final User receiver, final char mode) {
        channel.getUsers().get(receiver).giveMode(mode);

        callPlugin(new PluginCallback("onGiveMode") {
            void callback(Plugin plugin) {
                plugin.onGiveMode(giver, channel, receiver, mode);
            };
        });
    }

    /**
     * Invite received from known user
     * @param channel
     */
    void onInvite(final User user, final String channel) {
        callPlugin(new PluginCallback("onInvite") {
            void callback(Plugin plugin) {
                plugin.onInvite(user, channel);
            };
        });
    }

    /**
     * Invite received from unknown user
     * @param channel
     */
    void onInvite(String nickname, String channel) {
        onInvite(new User(nickname, this), channel);
    }

    /**
     * Someone did a kick
     * @param kicker
     * @param channel
     * @param receiver
     * @param message
     */
    void onKick(final User kicker, final Channel channel, final User receiver, final String message) {
        callPlugin(new PluginCallback("onKick") {
            void callback(Plugin plugin) {
                plugin.onKick(kicker, channel, receiver, message);
            };
        });
    }

    /**
     * Notice to channel received from known user
     * @param channel
     * @param message
     */
    void onNotice(final User user, final Channel channel, final String message) {
        callPlugin(new PluginCallback("onNotice") {
            void callback(Plugin plugin) {
                plugin.onNotice(user, channel, message);
            };
        });
    }

    /**
     * private notice from known user to us received
     * @param user sender
     * @param message
     */
    void onNotice(final User user, final String message) {
        callPlugin(new PluginCallback("onNotice") {
            void callback(Plugin plugin) {
                plugin.onNotice(user, message);
            };
        });
    }

    /**
     * notice to channel from unknown user (the user is not in the channel)
     * @param nickname
     * @param channel
     * @param message
     */
    void onNotice(String nickname, Channel channel, String message) {
        onNotice(new User(nickname, this), channel, message);
    }

    /**
     * private notive from unknown user
     * @param nickname
     * @param message
     */
    void onNotice(String nickname, String message) {
        onNotice(new User(nickname, this), message);
    }

    /**
     * We parted a channel
     * @param channel
     * @param message
     */
    void onPart(final Channel channel, final String message) {
        removeChannel(channel.getName());

        callPlugin(new PluginCallback("onPart") {
            void callback(Plugin plugin) {
                plugin.onPart(channel, message);
            };
        });
    }

    /**
     * Someone leaves a channel
     * @param user
     * @param channel
     * @param message
     */
    void onPart(final User user, final Channel channel, final String message) {
        channel.delUser(user);

        callPlugin(new PluginCallback("onPart") {
            void callback(Plugin plugin) {
                plugin.onPart(user, channel, message);
            };
        });
    }

    /**
     * Ping question received
     * @param target
     */
    void onPing(final String target) {
        callPlugin(new PluginCallback("onPing") {
            void callback(Plugin plugin) {
                plugin.onPing(target);
            };
        });
    }

    /**
     * Private message from unknown user
     * @param nickname
     * @param message
     */
    void onPrivmsg(String from, String to, final String message) {
        /* ignore everything we send to ourself to prevent sending loops */
        if (from == to)
            return;

        // Private message
        if (to.equals(getMyUser().getNickname())) {
            final User user = getUser(from) != null ? getUser(from) : new User(from, this);
            callPlugin(new PluginCallback("onPrivms") {
                void callback(Plugin plugin) {
                    plugin.onPrivmsg(user, message);
                };
            });
        } else if (from.equals(getMyUser().getNickname())) {
            final User user = getUser(to) != null ? getUser(to) : new User(to, this);
            callPlugin(new PluginCallback("onSendmsg") {
                void callback(Plugin plugin) {
                    plugin.onSendmsg(user, message);
                };
            });
            // To channel
        } else {
            final Channel channel = getChannel(to);
            final User user = getUser(from);

            if (user != null && channel != null) {
                callPlugin(new PluginCallback("onPrivmsg") {
                    void callback(Plugin plugin) {
                        plugin.onPrivmsg(user, channel, message);
                    };
                });
            } else {
                // TODO: should not happend
                System.out.println("Message from unkown person or to unknown channel");
                return;
            }
        }
    }

    /**
     * Someone quits the network
     * @param user
     * @param message
     */
    void onQuit(final User user, final String message) {
        /* remove the user from all channels */
        for (Channel channel : channels.values()) {
            channel.delUser(user);
        }

        /* remove user from user list */
        removeUser(user.getNickname());

        /* run the onQuit in plugins */
        callPlugin(new PluginCallback("onQuit") {
            void callback(Plugin plugin) {
                plugin.onQuit(user, message);
            };
        });
    }

    /**
     * Called when someone removes a mode from a user in a channel
     * @param channel
     * @param user
     * @param mode
     */
    void onTakeMode(final User taker, final Channel channel, final User affectedUser, final char mode) {
        channel.getUsers().get(affectedUser).takeMode(mode);

        callPlugin(new PluginCallback("onTakeMode") {
            void callback(Plugin plugin) {
                plugin.onTakeMode(taker, channel, affectedUser, mode);
            };
        });
    }

    /**
     * User joined a channel
     * @param user
     * @param channel
     */
    void onJoin(String nickname, String channelName) {

        User user = getUser(nickname);
        Channel channel = getChannel(channelName);

        // Its'a me mario! (It's me joining)
        if (user != null && user.equals(getMyUser())) {
            System.out.println("Half-joining channel " + channelName);
            // Add channel to unjoined channels
            channel = new Channel(channelName, this);
            unjoinedChannels.put(channelName, channel);
            // Other user is joining
        } else {

            // Uknown channel (should not happend at all)
            if (channel == null) {
                // FAAAAIIL: TODO: error check, this should not happend
                System.out.println("JOIN: Channel was null! " + channelName + ", user: " + nickname);
                return;
                // Known channel
            } else {

                // Have not seen this user before
                if (user == null) {
                    user = new User(nickname, this);
                    addUser(user);
                    System.out.println("New unknown user joining channel " + channelName);
                }
                System.out.println("New known user " + user + " joining channel " + channel);
                // Add user
                channel.addUser(user, new Modes());
                // Tell our fine plugins about this joyus occation and let them
                // rejoice and be happy.
                final User finalUser = user;
                final Channel finalChannel = channel;
                callPlugin(new PluginCallback("onJoin") {
                    void callback(Plugin plugin) {
                        plugin.onJoin(finalUser, finalChannel);
                    };
                });
            }
        }
    }

    /**
     * Called when someone changes nickname
     * @param nickname
     * @param newNickname
     */
    void onNick(String nickname, String newNickname) {

        // TODO: can this happend without a valid user?
        final User user = getUser(nickname);
        final String oldNickname = user.getNickname();

        // Move user in user hash map
        users.remove(oldNickname);
        users.put(newNickname, user);

        // Update channels

        user.setNickname(newNickname);

        callPlugin(new PluginCallback("onNick") {
            void callback(Plugin plugin) {
                plugin.onNick(user, oldNickname);
            };
        });
    }

    /**
     * Called on topic change
     * @param channel
     * @param newTopic
     */
    void onTopic(String channelName, String newTopic) {

        System.out.println("New topic for " + channelName + " is " + newTopic);

        final Channel channel = getChannel(channelName);

        if (channel != null) {
            final String oldTopic = channel.getTopic();
            channel.setTopic(newTopic);

            callPlugin(new PluginCallback("onTopic") {
                void callback(Plugin plugin) {
                    plugin.onTopic(me, channel, oldTopic);
                };
            });
        } else {
            // TODO: check unavaible list?
            Channel unjoinedChannel = unjoinedChannels.get(channelName);
            if (unjoinedChannel != null) {
                unjoinedChannel.setTopic(newTopic);
            } else {
                System.out.println("Error: Topic for non joined and non unjoined channel " + channelName);
            }
        }
    }

    /**
     * Called on connection successful
     */
    void onConnect() {
        connected = true;
        addUser(me);

        callPlugin(new PluginCallback("onConnected") {
            void callback(Plugin plugin) {
                plugin.onConnected();
            };
        });

        for (Map.Entry<String, Channel> entry : unjoinedChannels.entrySet()) {
            getConnection().join(entry.getKey(), entry.getValue().password);
        }
    }

    /**
     * Called when receiving a list of names from join or names command
     * @param channel
     * @param nicksWithModes
     */
    void onNames(String channelName, String[] nicksWithModes) {
        Channel channel = getChannel(channelName);

        if (channel == null) {
            channel = unjoinedChannels.get(channelName);
        }

        for (int i = 0; i < nicksWithModes.length; i++) {
            String nickname = nicksWithModes[i];

            if (nickname.startsWith("+") || nickname.startsWith("@") || nickname.startsWith("&")
                    || nickname.startsWith("~")) {
                if (!existsUser(nickname.substring(1))) {
                    addUser(new User(nickname.substring(1), this));
                }
                if (nickname.startsWith("+")) {
                    channel.addUser(getUser(nickname.substring(1)), new Modes("v"));
                } else if (nickname.startsWith("@") || nickname.startsWith("&") || nickname.startsWith("~")) {
                    channel.addUser(getUser(nickname.substring(1)), new Modes("o"));
                }
            } else {
                if (!existsUser(nickname)) {
                    addUser(new User(nickname, this));
                }
                channel.addUser(getUser(nickname), new Modes());
            }
            //System.out.println("*** Added user " + user + " to c " + c.getName() + ".");
        }

        // Notify plugins?
    }

    void onEndOfNames(String channelName) {

        // We are done joining
        if (unjoinedChannels.containsKey(channelName)) {
            System.out.println("Done half-joining channel " + channelName);
            Channel channel = unjoinedChannels.remove(channelName);
            channels.put(channelName, channel);

            for (Plugin plugin : plugins.values()) {
                try {
                    plugin.onJoin(channel);
                } catch (Exception e) {
                    System.out.println("Plugin " + plugin + "crashed in onJoin handler:");
                    System.out.println(e.getMessage());
                    e.printStackTrace();
                }
            }
        }

        // TODO: fix
    }

    // TODO: User Manager? {
    /**
     * Search for a user with a specified nickname
     *
     * @param nickname - The nickname of the user to search for
     * @return true iff the user with the specified nick exist
     */
    public boolean existsUser(String nickname) {
        return users.containsKey(nickname);
    }

    /**
     * Fetch a User with a specified nickname
     *
     * @param nickname - The nickname of the user to fetch
     * @return The user with the specified nickname
     */
    public User getUser(String nickname) {
        return users.get(nickname);
    }

    /**
     * Remove a User from the user list
     *
     * @param nickname - The nickname of the user to delete
     */
    private void removeUser(String nickname) {
        users.remove(nickname);
    }

    /**
     * Fetch a map of the users known on the Network
     *
     * @return A map containing all known users on the Network. With nickname as key and user object as value.
     */
    public Map<String, User> getUsers() {
        return users;
    }

    /**
     * Add a user to the Network
     *
     * @param u the user object
     */
    void addUser(User u) {
        users.put(u.getNickname(), u);
    }

    /**
     * Return yourself
     *
     * @return the user representing yourself
     */
    public User getMyUser() {
        return me;
    }
    // } END User Manager

    // TODO: Channel Manager? {
    /**
     * Fetch a collection containing all joined channels on the Network
     *
     * @return all known channels on the Network
     */
    public Collection<Channel> getChannels() {
        return channels.values();
    }

    /**
     * Get a reference to the top level.
     *
     * This is where the settings manage is available
     *
     * @return the IRC object
     */
    public IRC getIrc() {
        return irc;
    }

    /**
     * Get the network settings for this connection.
     *
     * This includes the name of the network, hostname and port of server and
     * so on...
     *
     * @return the network settings
     */
    public NetworkSettings getNetworkSettings() {
        return networkSettings;
    }

    /**
     * Get the configured plugins for this network
     *
     * @return  the plugins
     */
    public Map<String, Plugin> getPlugins() {
        return plugins;
    }

    /**
     * Check whether silvertrout is in a specified channel.
     *
     * @param   name  The name of the channel to search for
     * @return        True if the channel with the specified name is a channel
     *                that silvertrout is in.
     */
    public boolean isInChannel(String name) {
        return channels.containsKey(name);
    }

    /**
     * Returns a channel with a specified name
     *
     * @param  name The name of the channel to search fo
     * @return      The channel with the specified name, orr null if the
     *              channel does not exist
     */
    public Channel getChannel(String name) {
        return channels.get(name);
    }

    /**
     * Add a channel in the Network.
     *
     * @param channel - The the channel to add.
     */
    void addChannel(Channel channel) {
        if (!isInChannel(channel.getName())) {
            channels.put(channel.getName(), channel);
        }
    }

    /**
     * Remove a channel from the Network.
     *
     * @param channel - The name of the channel to remove from.
     */
    void removeChannel(String channel) {
        if (channels.remove(channel) != null) {
            System.out.println("Parting from channel " + channel);
        }
    }
    // } END CHANNEL MANAGER (TODO ? )

    /**
     * Unload a plugin with the spicified name.
     *
     * @param  name  The name of the plugin to unload
     * @return       True if the plugin was unloaded, otherwise false
     */
    public synchronized boolean unloadPlugin(String name) {
        synchronized (plugins) {
            if (plugins.containsKey(name)) {
                plugins.get(name).onUnload();
                plugins.remove(name);

                // Clean up after us a bit (to allow reload)
                // XXX: Is this really neccessary?
                Runtime.getRuntime().runFinalization();
                while (true) {
                    long freeMemory = Runtime.getRuntime().freeMemory();
                    Runtime.getRuntime().gc();
                    if (freeMemory == Runtime.getRuntime().freeMemory()) {
                        break;
                    }
                }

                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * Load plugin with the specified name from a file.
     *
     * @param  name  Name of the plugin to load
     * @return       True if the plugin was loaded, otherwise false
     */
    public synchronized boolean loadPlugin(String name) {
        if (!plugins.containsKey(name)) {
            synchronized (plugins) {
                try {
                    PluginClassLoader pcl = new PluginClassLoader();
                    Class<?> c = pcl.findClass("silvertrout.plugins." + name.toLowerCase() + "." + name);
                    if (Plugin.class.isAssignableFrom(c)) {
                        Plugin p = (Plugin) c.newInstance();
                        p.setNetwork(this);
                        p.onLoad(getIrc().getSettings().getPluginSettingsFor(getNetworkSettings().getName(), name));
                        plugins.put(name, p);
                        return true;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    /**
     * Call the callback in the PluginCallback for every loaded plugin.
     *
     * TODO: logging, exception handeling, exception frequency, etc
     *
     * @param  pcb  Plugin callback to call for every loaded plugin
     */
    public void callPlugin(PluginCallback pcb) {
        for (Plugin plugin : plugins.values()) {
            try {
                pcb.callback(plugin);
            } catch (Exception e) {
                System.out.println("Plugin " + plugin + "crashed in " + pcb.getCallbackName() + " handler:");
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * Get an indicator of the number of exceptions occuring in this network's working thread.
     * 0 is good. Every exception adds one and every tick() removes one.
     *
     * @return The exception frequencey.
     */
    public int getExceptionFrequenzy() {
        return exceptionFrequenzy;
    }

    /**
     * Get the current tick.
     *
     * @return  the current tick (i.e. seconds since silvertrout started)
     */
    public int getTick() {
        return tick;
    }

    /**
     *
     *
     */
    private void onTick(final int ticks) {

        if (!connected) {
            return;
        }

        tick = ticks;
        if (exceptionFrequenzy > 0) {
            exceptionFrequenzy--;
        }

        callPlugin(new PluginCallback("onTick") {
            void callback(Plugin plugin) {
                plugin.onTick(ticks);
            };
        });
    }

    /**
     * Get a handle for the worker thread. This should be used by caution since
     * this exposes the inner "pricate" class workerthread.
     * This is used by IRCconnection to add new incommin messages for handeling.
     *
     * @return  the worker thread
     */
    WorkerThread getWorkerThread() {
        return workerThread;
    }

    /**
     * Get the connection to this network.
     * Use this to send messages and other stuff
     *
     * @return  the connection to the network
     */
    public IRCConnection getConnection() {
        return connection;
    }

    /**
     *
     */
    public class WorkerThread extends Thread {

        private final ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<Runnable>();
        private final Semaphore taskSempaphore = new Semaphore(0);
        private final IRCProcessor processor = new IRCProcessor(Network.this);
        private final Timer tickTimer = new Timer("Tick timer");
        private int ticks = 0;
        private boolean stop = false;

        /**
         * Put something in queue to be invoked by the network thread.
         *
         * @param  task  thing to do
         */
        public void invokeLater(Runnable task) {
            tasks.add(task);
            taskSempaphore.release();
        }

        /**
         * Process an incoming message from the IRCConnection
         *
         * @param  msg  message to process
         */
        public void process(final Message msg) {
            invokeLater(new Runnable() {

                @Override
                public void run() {
                    processor.process(msg);
                }
            });
        }

        /**
         * Terminates the worker thread and the tick timer.
         * Does not let the workerthread process its queue.
         * This method is not thread safe and should be invoked by the network thread.
         */
        public void cancel() {
            stop = true;
            tickTimer.cancel();
            workerThread.interrupt(); // will this work? I dont think so. Cancel is run by the workerThread. Doesn't matter.
        }

        /**
         * Checks if the thread stopped.
         *
         * @return  True if the thread is stopped
         */
        public boolean isStop() {
            return stop;
        }

        @Override
        public void run() { // Wroker thread started!

            TimerTask ticker = new TimerTask() {

                @Override
                public void run() { // Executed by timer

                    invokeLater(new Runnable() { // Give network thread a new task

                        @Override
                        public void run() { // Executed by network thread
                            onTick(ticks);
                            ticks++;
                        }
                    });
                }
            };
            tickTimer.schedule(ticker, 1000, 1000); // wait one second before first tick to make the exceptionFrequenzy work.

            for (;;) {
                try {
                    if (!stop) {
                        taskSempaphore.acquire();
                        tasks.poll().run();
                    } else {
                        return;
                    }
                } catch (InterruptedException ex) {
                    Logger.getLogger(Network.class.getName()).log(Level.SEVERE, null, ex);
                    cancel(); // stop the thread and die...
                }
            }
        }
    }
}