Ignore:
Timestamp:
01/15/10 22:13:25 (2 years ago)
Author:
octorian
Message:

Initial implementation of improved network-interface selection (#146). Contains all the new configuration options and necessary refactoring, but only an initial attempt at actual pre-5.0 connection logic.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/LogicMail/src/org/logicprobe/LogicMail/util/Connection.java

    r477 r608  
    6161 
    6262import net.rim.device.api.crypto.tls.tls10.TLS10Connection; 
     63import net.rim.device.api.i18n.ResourceBundle; 
    6364import net.rim.device.api.system.EventLogger; 
    64 import net.rim.device.api.ui.UiApplication; 
    65 import net.rim.device.api.ui.component.Dialog; 
    6665import net.rim.device.api.util.DataBuffer; 
    6766import net.rim.device.cldc.io.ssl.TLSException; 
    6867 
    6968import org.logicprobe.LogicMail.AppInfo; 
     69import org.logicprobe.LogicMail.LogicMailResource; 
     70import org.logicprobe.LogicMail.conf.ConnectionConfig; 
    7071import org.logicprobe.LogicMail.conf.GlobalConfig; 
    7172import org.logicprobe.LogicMail.conf.MailSettings; 
     
    7778import java.io.OutputStream; 
    7879 
    79 import java.util.Vector; 
    80  
    81 import javax.microedition.io.Connector; 
    8280import javax.microedition.io.SocketConnection; 
    8381import javax.microedition.io.StreamConnection; 
     
    102100 * is the only way to get rid of compile-time dependencies on these classes. 
    103101 */ 
    104 public class Connection { 
     102public abstract class Connection { 
     103    protected static ResourceBundle resources = ResourceBundle.getBundle(LogicMailResource.BUNDLE_ID, LogicMailResource.BUNDLE_NAME); 
     104     
     105    /** Select everything except WiFi */ 
     106    protected static final int TRANSPORT_AUTO = 0xFE; 
     107    /** Select WiFi */ 
     108    protected static final int TRANSPORT_WIFI = 0x01; 
     109    /** Select Direct TCP */ 
     110    protected static final int TRANSPORT_DIRECT_TCP = 0x02; 
     111    /** Select MDS */ 
     112    protected static final int TRANSPORT_MDS = 0x04; 
     113    /** Select WAP 2.0 */ 
     114    protected static final int TRANSPORT_WAP2 = 0x08; 
     115 
    105116    /** 
    106117     * Byte array holding carriage return and line feed 
     
    108119    private static final byte[] CRLF = new byte[] { 13, 10 }; 
    109120 
    110     /** 
    111      * Holds a list of open connections 
    112      */ 
    113     private static Vector openConnections = new Vector(); 
    114     private String serverName; 
    115     private int serverPort; 
    116     private boolean useSSL; 
    117     private boolean deviceSide; 
     121    private static UtilFactory connectionFactory; 
     122    protected String serverName; 
     123    protected int serverPort; 
     124    protected boolean useSSL; 
     125    protected int transports; 
    118126    private StreamConnection socket; 
    119127    private String localAddress; 
    120     private GlobalConfig globalConfig; 
     128    protected GlobalConfig globalConfig; 
    121129    protected InputStream input; 
    122130    protected OutputStream output; 
    123     private boolean useWiFi; 
     131    protected boolean useWiFi; 
    124132    private int fakeAvailable = -1; 
    125133    private int bytesSent = 0; 
    126134    private int bytesReceived = 0; 
    127      
     135 
    128136    /** 
    129137     * Provides a buffer used for incoming data. 
     
    136144    DataBuffer resultBuffer = new DataBuffer(); 
    137145 
    138     public Connection(String serverName, int serverPort, boolean useSSL, 
    139         boolean deviceSide) { 
    140         this.serverName = serverName; 
    141         this.serverPort = serverPort; 
    142         this.useSSL = useSSL; 
    143         this.deviceSide = deviceSide; 
     146    /** 
     147     * Initializes a new connection object. 
     148     *  
     149     * @param connectionConfig Configuration data for the connection 
     150     */ 
     151    protected Connection(ConnectionConfig connectionConfig) { 
     152        this.globalConfig = MailSettings.getInstance().getGlobalConfig(); 
     153         
     154        this.serverName = connectionConfig.getServerName(); 
     155        this.serverPort = connectionConfig.getServerPort(); 
     156        this.useSSL = (connectionConfig.getServerSecurity() == ConnectionConfig.SECURITY_SSL); 
     157         
     158        int transportType; 
     159        boolean enableWiFi; 
     160        if(connectionConfig.getTransportType() == ConnectionConfig.TRANSPORT_GLOBAL) { 
     161            transportType = globalConfig.getTransportType(); 
     162            enableWiFi = globalConfig.getEnableWiFi(); 
     163        } 
     164        else { 
     165            transportType = connectionConfig.getTransportType(); 
     166            enableWiFi = connectionConfig.getEnableWiFi(); 
     167        } 
     168 
     169        // Populate the bit-flags for the selected transport types 
     170        // based on the configuration parameters. 
     171        switch(transportType) { 
     172        case ConnectionConfig.TRANSPORT_AUTO: 
     173            transports = Connection.TRANSPORT_AUTO; 
     174            break; 
     175        case ConnectionConfig.TRANSPORT_DIRECT_TCP: 
     176            transports = Connection.TRANSPORT_DIRECT_TCP; 
     177            break; 
     178        case ConnectionConfig.TRANSPORT_MDS: 
     179            transports = Connection.TRANSPORT_MDS; 
     180            break; 
     181        case ConnectionConfig.TRANSPORT_WAP2: 
     182            transports = Connection.TRANSPORT_WAP2; 
     183            break; 
     184        default: 
     185            // Should only get here in rare cases of invalid configuration 
     186            // data, so we select full automatic with WiFi. 
     187            transports = Connection.TRANSPORT_AUTO; 
     188            enableWiFi = true; 
     189            break; 
     190        } 
     191        if(enableWiFi) { transports |= Connection.TRANSPORT_WIFI; } 
     192         
    144193        this.input = null; 
    145194        this.output = null; 
    146195        this.socket = null; 
    147         this.globalConfig = MailSettings.getInstance().getGlobalConfig(); 
    148     } 
    149  
     196    } 
     197 
     198    /** 
     199     * Sets the connection factory reference. 
     200     *  
     201     * @param connectionFactory connection factory reference 
     202     */ 
     203    static void setConnectionFactory(UtilFactory connectionFactory) { 
     204        Connection.connectionFactory = connectionFactory; 
     205    } 
     206     
    150207    /** 
    151208     * Opens a connection. 
     
    155212            close(); 
    156213        } 
    157  
    158         synchronized (openConnections) { 
    159             if (!openConnections.contains(this)) { 
    160                 openConnections.addElement(this); 
    161             } 
    162         } 
    163  
    164         String protocolStr = (useSSL ? "ssl" : "socket"); 
    165  
    166         // This parameter, which allows bypassing the MDS proxy, should probably 
    167         // be a global user configurable option 
    168         String paramStr = (deviceSide ? ";deviceside=true" : ""); 
    169  
    170         useWiFi = false; 
    171  
    172         if (globalConfig.getWifiMode() == GlobalConfig.WIFI_PROMPT) { 
    173             UiApplication.getUiApplication().invokeAndWait(new Runnable() { 
    174                     public void run() { 
    175                         useWiFi = (Dialog.ask(Dialog.D_YES_NO, 
    176                                 "Connect through WiFi?") == Dialog.YES); 
    177                     } 
    178                 }); 
    179         } else if (globalConfig.getWifiMode() == GlobalConfig.WIFI_ALWAYS) { 
    180             useWiFi = true; 
    181         } 
    182  
    183         if (useWiFi) { 
    184             paramStr = paramStr + ";interface=wifi"; 
    185         } 
    186  
    187         String connectStr = protocolStr + "://" + serverName + ":" + 
    188             serverPort + paramStr; 
    189  
    190         if (EventLogger.getMinimumLevel() >= EventLogger.INFORMATION) { 
    191             String msg = "Opening connection:\r\n" + connectStr + "\r\n"; 
    192             EventLogger.logEvent(AppInfo.GUID, msg.getBytes(), 
    193                 EventLogger.INFORMATION); 
    194         } 
    195  
    196         socket = (StreamConnection) Connector.open(connectStr, 
    197                 Connector.READ_WRITE, true); 
     214         
     215        connectionFactory.addOpenConnection(this); 
     216 
     217        socket = openStreamConnection(); 
     218        if(socket == null) { 
     219            throw new IOException(resources.getString(LogicMailResource.ERROR_UNABLE_TO_OPEN_CONNECTION)); 
     220        } 
     221         
    198222        input = socket.openDataInputStream(); 
    199223        output = socket.openDataOutputStream(); 
     
    201225        bytesSent = 0; 
    202226        bytesReceived = 0; 
    203          
     227 
    204228        if (EventLogger.getMinimumLevel() >= EventLogger.INFORMATION) { 
    205229            String msg = "Connection established:\r\n" + "Socket: " + 
    206                 socket.getClass().toString() + "\r\n" + "Local address: " + 
    207                 localAddress + "\r\n"; 
     230            socket.getClass().toString() + "\r\n" + "Local address: " + 
     231            localAddress + "\r\n"; 
    208232            EventLogger.logEvent(AppInfo.GUID, msg.getBytes(), 
    209                 EventLogger.INFORMATION); 
    210         } 
    211     } 
     233                    EventLogger.INFORMATION); 
     234        } 
     235    } 
     236     
     237    /** 
     238     * Open a stream connection. 
     239     * This method should encapsulate all platform-specific logic for opening 
     240     * network connections. 
     241     *  
     242     * @return the stream connection 
     243     *  
     244     * @throws IOException Signals that an I/O exception has occurred. 
     245     */ 
     246    protected abstract StreamConnection openStreamConnection() throws IOException; 
    212247 
    213248    /** 
     
    242277        } 
    243278 
    244         synchronized (openConnections) { 
    245             if (openConnections.contains(this)) { 
    246                 openConnections.removeElement(this); 
    247             } 
    248         } 
     279        connectionFactory.removeOpenConnection(this); 
    249280 
    250281        EventLogger.logEvent(AppInfo.GUID, "Connection closed".getBytes(), 
    251             EventLogger.INFORMATION); 
    252     } 
    253  
    254     /** 
    255      * Determine whether open connections exist 
    256      * 
    257      * @return True if there are open connections 
    258      */ 
    259     public static boolean hasOpenConnections() { 
    260         boolean result; 
    261  
    262         synchronized (openConnections) { 
    263             result = !openConnections.isEmpty(); 
    264         } 
    265  
    266         return result; 
    267     } 
    268  
    269     /** 
    270      * Close all open connections 
    271      */ 
    272     public static void closeAllConnections() { 
    273         synchronized (openConnections) { 
    274             int size = openConnections.size(); 
    275  
    276             for (int i = 0; i < size; i++) { 
    277                 try { 
    278                     ((Connection) openConnections.elementAt(i)).close(); 
    279                 } catch (IOException e) { 
    280                 } 
    281             } 
    282  
    283             openConnections.removeAllElements(); 
    284         } 
     282                EventLogger.INFORMATION); 
    285283    } 
    286284 
     
    324322     */ 
    325323    public int getBytesSent() { 
    326         return bytesSent; 
    327     } 
    328      
     324        return bytesSent; 
     325    } 
     326 
    329327    /** 
    330328     * Gets the number of bytes that have been received since the 
     
    338336     */ 
    339337    public int getBytesReceived() { 
    340         return bytesReceived; 
    341     } 
    342      
     338        return bytesReceived; 
     339    } 
     340 
    343341    /** 
    344342     * Sends a string to the server. This method is used internally for 
     
    360358            if (globalConfig.getConnDebug()) { 
    361359                EventLogger.logEvent(AppInfo.GUID, "[SEND]".getBytes(), 
    362                     EventLogger.DEBUG_INFO); 
     360                        EventLogger.DEBUG_INFO); 
    363361            } 
    364362 
     
    386384                if (globalConfig.getConnDebug()) { 
    387385                    EventLogger.logEvent(AppInfo.GUID, 
    388                         ("[SEND] " + s.substring(i, j)).getBytes(), 
    389                         EventLogger.DEBUG_INFO); 
     386                            ("[SEND] " + s.substring(i, j)).getBytes(), 
     387                            EventLogger.DEBUG_INFO); 
    390388                } 
    391389 
     
    420418        if (globalConfig.getConnDebug()) { 
    421419            EventLogger.logEvent(AppInfo.GUID, ("[SEND CMD] " + s).getBytes(), 
    422                 EventLogger.DEBUG_INFO); 
     420                    EventLogger.DEBUG_INFO); 
    423421        } 
    424422 
     
    427425            bytesSent += 2; 
    428426        } else { 
    429                 byte[] buf = (s + "\r\n").getBytes(); 
     427            byte[] buf = (s + "\r\n").getBytes(); 
    430428            output.write(buf); 
    431429            bytesSent += buf.length; 
     
    448446        if (globalConfig.getConnDebug()) { 
    449447            EventLogger.logEvent(AppInfo.GUID, 
    450                 ("[SEND RAW]\r\n" + s).getBytes(), EventLogger.DEBUG_INFO); 
     448                    ("[SEND RAW]\r\n" + s).getBytes(), EventLogger.DEBUG_INFO); 
    451449        } 
    452450 
    453451        output.write(buf, 0, buf.length); 
    454452        bytesSent += buf.length; 
    455          
     453 
    456454        output.flush(); 
    457455    } 
     
    530528            while (true) { 
    531529                int actual = input.read(buffer, count, 1); 
    532                  
     530 
    533531                /** 
    534532                 * If -1 is returned, the InputStream is already closed, 
     
    539537                if (actual == -1) { 
    540538                    EventLogger.logEvent(AppInfo.GUID, 
    541                         "Unable to read from socket, closing connection".getBytes(), 
    542                         EventLogger.INFORMATION); 
     539                            "Unable to read from socket, closing connection".getBytes(), 
     540                            EventLogger.INFORMATION); 
    543541 
    544542                    try { 
     
    567565                // approach which screws up on mid-line LFs. (DK) 
    568566                else { 
    569                         bytesReceived += actual; 
    570                          
     567                    bytesReceived += actual; 
     568 
    571569                    byte b = buffer[count]; 
    572570                    readBytes++; 
     
    606604        if (globalConfig.getConnDebug()) { 
    607605            EventLogger.logEvent(AppInfo.GUID, 
    608                 ("[RECV] " + result).getBytes(), 
    609                 EventLogger.DEBUG_INFO); 
     606                    ("[RECV] " + result).getBytes(), 
     607                    EventLogger.DEBUG_INFO); 
    610608        } 
    611609 
     
    619617    } 
    620618 
    621         /** 
    622         * Switches the underlying connection to SSL mode, as commonly done after 
    623         * sending a <tt>STARTTLS</tt> command to the server. 
    624         *  
    625         * @throws IOException Signals that an I/O exception has occurred. 
    626         */ 
    627         public void startTLS() throws IOException { 
    628                 // Shortcut the method if we're already in SSL mode 
    629                 if(socket instanceof TLS10Connection) { return; } 
    630                  
    631                 try { 
    632                         TLS10Connection tlsSocket = new TLS10Connection( 
    633                                         new StreamConnectionWrapper( 
    634                                                 socket, 
    635                                                 (DataInputStream)input, 
    636                                                 (DataOutputStream)output), 
    637                                         serverName + ':' + serverPort, 
    638                                         true); 
    639                          
    640                         socket = tlsSocket; 
    641                         input = socket.openDataInputStream(); 
    642                         output = socket.openDataOutputStream(); 
    643                 } catch (IOException e) { 
     619    /** 
     620    * Switches the underlying connection to SSL mode, as commonly done after 
     621    * sending a <tt>STARTTLS</tt> command to the server. 
     622    *  
     623    * @throws IOException Signals that an I/O exception has occurred. 
     624    */ 
     625    public void startTLS() throws IOException { 
     626        // Shortcut the method if we're already in SSL mode 
     627        if(socket instanceof TLS10Connection) { return; } 
     628 
     629        try { 
     630            TLS10Connection tlsSocket = new TLS10Connection( 
     631                    new StreamConnectionWrapper( 
     632                            socket, 
     633                            (DataInputStream)input, 
     634                            (DataOutputStream)output), 
     635                            serverName + ':' + serverPort, 
     636                            true); 
     637 
     638            socket = tlsSocket; 
     639            input = socket.openDataInputStream(); 
     640            output = socket.openDataOutputStream(); 
     641        } catch (IOException e) { 
    644642            EventLogger.logEvent(AppInfo.GUID, 
    645643                    ("Unable to switch to TLS mode: " + e.getMessage()).getBytes(), EventLogger.ERROR); 
    646644            throw new IOException("Unable to switch to TLS mode"); 
    647                 } catch (TLSException e) { 
     645        } catch (TLSException e) { 
    648646            EventLogger.logEvent(AppInfo.GUID, 
    649647                    ("Unable to switch to TLS mode: " + e.getMessage()).getBytes(), EventLogger.ERROR); 
    650648            throw new IOException("Unable to switch to TLS mode"); 
    651                 } 
    652         } 
    653          
    654         /** 
    655         * Decorator to wrap an existing stream connection so its I/O streams 
    656         * can be reopened without throwing exceptions. 
    657         */ 
    658         private static class StreamConnectionWrapper implements StreamConnection { 
    659                 private StreamConnection stream; 
    660                 private DataInputStream dataInputStream; 
    661                 private DataOutputStream dataOutputStream; 
    662                  
    663                 public StreamConnectionWrapper(StreamConnection stream, DataInputStream dataInputStream, DataOutputStream dataOutputStream) { 
    664                         this.stream = stream; 
    665                         this.dataInputStream = dataInputStream; 
    666                         this.dataOutputStream = dataOutputStream; 
    667                 } 
    668                  
    669                 public DataInputStream openDataInputStream() throws IOException { 
    670                         return dataInputStream; 
    671                 } 
    672                 public InputStream openInputStream() throws IOException { 
    673                         return dataInputStream; 
    674                 } 
    675                 public void close() throws IOException { 
    676                         stream.close(); 
    677                 } 
    678                 public DataOutputStream openDataOutputStream() throws IOException { 
    679                         return dataOutputStream; 
    680                 } 
    681                 public OutputStream openOutputStream() throws IOException { 
    682                         return dataOutputStream; 
    683                 } 
    684         } 
     649        } 
     650    } 
     651 
     652    /** 
     653    * Decorator to wrap an existing stream connection so its I/O streams 
     654    * can be reopened without throwing exceptions. 
     655    */ 
     656    private static class StreamConnectionWrapper implements StreamConnection { 
     657        private StreamConnection stream; 
     658        private DataInputStream dataInputStream; 
     659        private DataOutputStream dataOutputStream; 
     660 
     661        public StreamConnectionWrapper(StreamConnection stream, DataInputStream dataInputStream, DataOutputStream dataOutputStream) { 
     662            this.stream = stream; 
     663            this.dataInputStream = dataInputStream; 
     664            this.dataOutputStream = dataOutputStream; 
     665        } 
     666 
     667        public DataInputStream openDataInputStream() throws IOException { 
     668            return dataInputStream; 
     669        } 
     670        public InputStream openInputStream() throws IOException { 
     671            return dataInputStream; 
     672        } 
     673        public void close() throws IOException { 
     674            stream.close(); 
     675        } 
     676        public DataOutputStream openDataOutputStream() throws IOException { 
     677            return dataOutputStream; 
     678        } 
     679        public OutputStream openOutputStream() throws IOException { 
     680            return dataOutputStream; 
     681        } 
     682    } 
    685683} 
Note: See TracChangeset for help on using the changeset viewer.