ConnectionService.java

/*
 * Copyright @ 2012 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.client;

import java.io.IOException;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

import com.nokia.example.btsppecho.ClientForm;
import com.nokia.example.btsppecho.LogScreen;

public class ConnectionService
        implements Runnable {

    private final ClientForm listener;
    private final String url;
    private StreamConnectionNotifier connectionNotifier = null;
    private volatile boolean aborting;

    public ConnectionService(String url,
            ClientForm listener) {
        this.url = url;
        this.listener = listener;

        LogScreen.log("ConnectionService: waiting to "
                + "accept connections on '"
                + url + "'\n");

        // start waiting for a connection
        Thread thread = new Thread(this);
        thread.start();
    }

    public String getClientURL() {
        return url;
    }

    public void close() {
        if (!aborting) {
            synchronized (this) {
                aborting = true;
            }

            // Ideally, one might want to give the run method's
            // loop a chance to abort before calling the
            // subsequent close, but the run loop is anyways
            // likely to be sitting on the acceptAndOpen
            // (i.e. blocked).

            try {
                connectionNotifier.close();
            } catch (IOException e) {
                // There is nothing very useful that
                // we can do for this case.
            }
        }
    }

    public void run() {
        aborting = false;

        try {
            connectionNotifier =
                    (StreamConnectionNotifier) Connector.open(url);

            // It might useful in some cases to add a service to the
            // 'Public Browse Group'. For example by doing something
            // approximately as follows:
            // -----------------------------------------------------
            // Retrieve the service record template
            // LocalDevice ld = LocalDevice.getLocalDevice();
            // ServiceRecord rec = ld.getRecord(connectionNotifier);
            // DataElement element =
            //             new DataElement(DataElement.DATSEQ);
            //
            // The service class for PublicBrowseGroup (0x1002)
            // is defined in the Bluetooth Assigned Numbers document.
            // element.addElement(new DataElement(DataElement.UUID,
            //                                    new UUID(0x1002)));
            //
            // Add to the public browse group:
            // rec.setAttributeValue(0x0005, element);
            // -----------------------------------------------------
        } catch (IOException e) {
            // ConnectionNotFoundException is an IOException
            String errorMessage =
                    "Error while starting ConnectionService: "
                    + e.getMessage();

            listener.handleError(null, errorMessage);

            aborting = true;
        } catch (SecurityException e) {
            String errorMessage =
                    "SecurityException while starting ConnectionService: "
                    + e.getMessage();

            listener.handleError(null, errorMessage);

            aborting = true;
        }

        while (!aborting) {
            try {
                // 1. wait to accept & open a new connection
                StreamConnection connection =
                        (StreamConnection) connectionNotifier.acceptAndOpen();

                LogScreen.log("ConnectionService: new connection\n");

                // 2. create a handler to take care of
                // the new connection and inform
                // the listener
                if (!aborting) {
                    ClientConnectionHandler handler =
                            new ClientConnectionHandler(this,
                            connection,
                            listener);
                    listener.handleAcceptAndOpen(handler);
                }

                // One could consider exiting the
                // ConnectionService when the Client
                // reaches the maximum number of allowed
                // open connections. In that case (i.e.
                // when the maximum number of possible
                // connections is already open), the
                // ConnectionService will not be able
                // to accept any new connections and one
                // might possibly want to consider whether
                // or not the ConnectionService thread
                // could then be terminated.
                //
                // However, existing connections can also
                // be disconnected (e.g. the Server is
                // terminated or closes some/all of its
                // existing connections). In that case,
                // one may want to keep the
                // ConnectionService alive and running:
                // in order to accept later new connections
                // without the need to restart the
                // ConnectionService or MIDlet.
                //
                // (This MIDlet uses the latter approach.)
            } catch (IOException e) {
                if (!aborting) {
                    String errorMessage =
                            "IOException occurred during "
                            + "accept and open: "
                            + e.getMessage();

                    listener.handleError(null, errorMessage);
                }
            } catch (SecurityException e) {
                if (!aborting) {
                    String errorMessage =
                            "IOException occurred during "
                            + "accept and open: "
                            + e.getMessage();

                    listener.handleError(null, errorMessage);
                }
            }
        }
    }
}