ServerForm.java

/*
 * Copyright © 2011 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. 
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners. 
 * See LICENSE.TXT for license information.
 */
package com.nokia.example.btsppecho;

import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.ServiceRecord;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;

import com.nokia.example.btsppecho.server.ServerConnectionHandler;
import com.nokia.example.btsppecho.server.ServerConnectionHandlerListener;

class ServerForm
        extends Form
        implements ServerConnectionHandlerListener, CommandListener {

    private final MIDletApplication midlet;
    private final StringItem numConnectionsField;
    private final TextField sendDataField;
    private final StringItem receivedDataField;
    private final StringItem statusField;
    private final Command sendCommand;
    private final Command addConnectionCommand;
    private final Command searchCommand;
    private final Command logCommand;
    private final Command quitCommand;
    private final Command clearStatusCommand;
    private final Vector handlers;
    private int maxConnections;
    private StringItem btAddressField = null;
    private volatile int numReceivedMessages = 0;
    private volatile int numSentMessages = 0;
    private int sendMessageId = 0;

    ServerForm(MIDletApplication midlet) {
        super("Server");
        this.midlet = midlet;
        handlers = new Vector();

        String value =
                LocalDevice.getProperty(
                "bluetooth.connected.devices.max");
        try {
            maxConnections = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            maxConnections = 0;
        }

        // 1. add Form items
        try {
            String address = LocalDevice.getLocalDevice().getBluetoothAddress();
            btAddressField = new StringItem("My address", address);
            append(btAddressField);
        } catch (BluetoothStateException e) {
            // nothing we can do, don't add field
        }
        numConnectionsField = new StringItem("Connections", "0");
        append(numConnectionsField);
        statusField = new StringItem("Status", "");
        append(statusField);
        sendDataField = new TextField("Send data",
                "Server says: 'Hello, world.'",
                64,
                TextField.ANY);
        append(sendDataField);
        receivedDataField = new StringItem("Last received data",
                null);
        append(receivedDataField);


        // 2. create commands
        sendCommand = new Command("Send", Command.SCREEN, 1);
        searchCommand = new Command("Search for more",
                Command.SCREEN,
                1);
        addConnectionCommand = new Command("Add connection",
                Command.SCREEN,
                2);
        logCommand = new Command("View log", Command.SCREEN, 3);
        clearStatusCommand = new Command("Clear status", Command.SCREEN, 4);
        quitCommand = new Command("Quit", Command.EXIT, 1);


        // 3. add commands and set command listener
        addCommand(searchCommand);
        addCommand(addConnectionCommand);
        addCommand(logCommand);
        addCommand(clearStatusCommand);
        addCommand(quitCommand);
        // The 'sendCommand' is added later to screen,
        // if at least one connection is open.
        setCommandListener(this);
    }

    public void makeConnections(Vector serviceRecords, int security) {
        for (int i = 0; i < serviceRecords.size(); i++) {
            ServiceRecord serviceRecord =
                    (ServiceRecord) serviceRecords.elementAt(i);
            boolean found = false;
            for (int j = 0; j < handlers.size(); j++) {
                ServerConnectionHandler old =
                        (ServerConnectionHandler) handlers.elementAt(j);
                String oldAddr = old.getServiceRecord().
                        getHostDevice().
                        getBluetoothAddress();
                String newAddr =
                        serviceRecord.getHostDevice().getBluetoothAddress();
                if (oldAddr.equals(newAddr)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                ServerConnectionHandler newHandler =
                        new ServerConnectionHandler(
                        this,
                        serviceRecord,
                        security);
                newHandler.start(); // start reader & writer
            }
        }
    }

    private void removeHandler(ServerConnectionHandler handler) {
        if (handlers.contains(handler)) {
            handlers.removeElement(handler);
            String str = Integer.toString(handlers.size());
            numConnectionsField.setText(str);
            if (handlers.size() == 0) {
                removeCommand(sendCommand);
                addCommand(searchCommand);
            }
        }
    }

    void closeAll() {
        for (int i = 0; i < handlers.size(); i++) {
            ServerConnectionHandler handler =
                    (ServerConnectionHandler) handlers.elementAt(i);
            handler.close();
            removeHandler(handler);
        }
    }

    public void commandAction(Command cmd, Displayable disp) {
        if (cmd == addConnectionCommand) {
            Vector v = new Vector();
            for (int i = 0; i < handlers.size(); i++) {
                ServerConnectionHandler handler =
                        (ServerConnectionHandler) handlers.elementAt(i);
                String btAddress = handler.getServiceRecord().getHostDevice().getBluetoothAddress();
                v.addElement(btAddress);
            }
            midlet.serverFormAddConnection(v);
        } else if (cmd == clearStatusCommand) {
            statusField.setText("");
        } else if (cmd == logCommand) {
            midlet.serverFormViewLog();
        } else if (cmd == sendCommand) {
            String sendData = sendDataField.getString();
            Integer id = new Integer(sendMessageId++);

            for (int i = 0; i < handlers.size(); i++) {
                ServerConnectionHandler handler =
                        (ServerConnectionHandler) handlers.elementAt(i);
                try {
                    handler.queueMessageForSending(
                            id,
                            sendData.getBytes());
                    statusField.setText(
                            "Queued a send message request");
                } catch (IllegalArgumentException e) {
                    // Message length longer than
                    // ServerConnectionHandler.MAX_MESSAGE_LENGTH

                    String errorMessage =
                            "IllegalArgumentException while trying "
                            + "to send a message: " + e.getMessage();
                    handleError(handler, errorMessage);
                }
            }
        } else if (cmd == searchCommand) {
            midlet.serverFormSearchRequest(handlers.size());
        } else if (cmd == quitCommand) {
            closeAll();
            midlet.serverFormExitRequest();
        }

        // To keep this MIDlet simple, I didn't add any way
        // to drop individual connections. But you might
        // want to do so.
    }

    // ServerConnectionHandlerListener interface methods
    public void handleOpen(ServerConnectionHandler handler) {
        handlers.addElement(handler);
        // for the first open connection
        if (handlers.size() == 1) {
            removeCommand(searchCommand);

            removeCommand(sendCommand);
            addCommand(sendCommand);
        }

        // Remove the 'Add connection' command
        // when the device already has open the
        // maximum number of connections it can
        // support.
        if (handlers.size() >= maxConnections) {
            removeCommand(addConnectionCommand);
        }

        statusField.setText("Connection opened");
        String str = Integer.toString(handlers.size());
        numConnectionsField.setText(str);
    }

    public void handleOpenError(
            ServerConnectionHandler handler,
            String errorMessage) {
        statusField.setText("Error opening outbound connection: "
                + errorMessage);
    }

    public void handleReceivedMessage(
            ServerConnectionHandler handler,
            byte[] messageBytes) {
        numReceivedMessages++;

        String message = new String(messageBytes);
        receivedDataField.setText(message);

        statusField.setText(
                "# messages read: " + numReceivedMessages + " "
                + "sent: " + numSentMessages);

        // Broadcast message to all clients
        for (int i = 0; i < handlers.size(); i++) {
            ServerConnectionHandler h =
                    (ServerConnectionHandler) handlers.elementAt(i);

            Integer id = new Integer(sendMessageId++);
            try {
                h.queueMessageForSending(id, messageBytes);
            } catch (IllegalArgumentException e) {
                String errorMessage =
                        "IllegalArgumentException while trying to "
                        + "send message: " + e.getMessage();
                handleError(handler, errorMessage);
            }
        }
    }

    public void handleQueuedMessageWasSent(
            ServerConnectionHandler handler,
            Integer id) {
        numSentMessages++;
        statusField.setText("# messages read: "
                + numReceivedMessages + " "
                + "sent: " + numSentMessages);
    }

    public void handleClose(ServerConnectionHandler handler) {
        removeHandler(handler);
        if (handlers.size() == 0) {
            removeCommand(sendCommand);
            addCommand(searchCommand);
        }

        // If the number of currently open connections
        // drops below the maximum number that this
        // device could have open, restore
        // 'Add connection' to the screen commands.
        if (handlers.size() < maxConnections) {
            removeCommand(addConnectionCommand);
            addCommand(addConnectionCommand);
        }

        statusField.setText("Connection closed");
    }

    public void handleErrorClose(ServerConnectionHandler handler,
            String errorMessage) {
        removeHandler(handler);
        if (handlers.size() == 0) {
            removeCommand(sendCommand);
            addCommand(searchCommand);
        }

        statusField.setText("Error (close): '" + errorMessage + "'");
    }

    public void handleError(ServerConnectionHandler handler,
            String errorMessage) {
        statusField.setText("Error: '" + errorMessage + "'");
    }
}