Java/OO techniques for modularity and re-use

R

Richard Maher

Hi,

Please help if you can (with probably a very basic OO newbie question): -

I have created a class that uses the java.net.Socket class to talk to my
server and everything is great. I then converted the code to use the
javax.net.SSLSocket class and (thanks to how easy Java makes it for us!)
everything is still great. What I want to do now is parameterize/optionalize
the use of SSL or in-the-clear Sockets within my class, and I'm struggling
to find a modular, let alone elegant, solution.

At the moment I plan to add another Applet parameter called SSL_REQD and I
will pass that to my constructor, but because of Java's compile-time
resolution of methods-to-objects, I find myself having to duplicate code
that is erstwhile 99% identical or common. Is there some way (short of
Reflection) that I can leverage the fact SSLSocket class inherits most of
its methods from the Socket class so that I only need one method for each
socket function regardless of what flavour socket is in use?

For example: -

private someSocket t3Sock;

if (sssReqd)
t3Sock = (SSLSocket)sockFactory.createSocket();
else
t3Sock = new Socket();

t3Sock.setKeepAlive(true);

Am I stuck with "One's an Apple and the other's an Orange (albeit painted
red :)"?

If few of the Socket methods are overridden by SSLSocket (and the
value-added encryption stuff happens at a lower/other level) can I just cast
my way around some of this? Just stick in a few "if" statements and stop
moanin'?

Cheers Richard Maher

Here is the complete Tier3Socket class definition: -

import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.net.SocketTimeoutException;
import java.lang.System;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class Tier3Socket
{
public static final
String T3ID="T3$";
public static final
int USERSIZ=40;
public static final
int T3IDBUFSIZ=48;
public static final
int CREDBUFSIZ=80;
public static final
int CONTIMOUT=10000;

public byte [] t3IdBuf;
public byte [] readBuf;
public byte [] writeBuf;

private String host;
private int port;
private int maxBufSiz;
private int bytesIn;
private String hostCharSet;
private SSLSocket t3Sock;
private SSLSocketFactory sockFactory;
private BufferedInputStream in;
private BufferedOutputStream out;
private byte [] outUser;
private byte [] outPwd;
private byte [] credBuf;
private String inMsg;
private String stringOut;

Tier3Socket (String host, int port, int maxBufSiz, String hostCharSet)
{
this.host = host;
this.port = port;
this.maxBufSiz = maxBufSiz;
this.hostCharSet = hostCharSet;
this.bytesIn = 0;

t3IdBuf = new byte[T3IDBUFSIZ];
readBuf = new byte[maxBufSiz];

sockFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
}

public void open() throws UnknownHostException, IOException
{
t3Sock = (SSLSocket)sockFactory.createSocket();
t3Sock.setKeepAlive(true);
t3Sock.setReuseAddress(true);
t3Sock.setTcpNoDelay(true);

t3Sock.connect(new InetSocketAddress(host,port), CONTIMOUT);

System.out.println("Connected OK");

in = new BufferedInputStream (t3Sock.getInputStream() ,maxBufSiz);
out = new BufferedOutputStream (t3Sock.getOutputStream(),maxBufSiz);

t3Sock.setUseClientMode(true);

System.out.println("Going for SSL");

try {t3Sock.startHandshake();}
catch (IOException e)
{
System.out.println("Failed SSL Handshake");
throw new IOException("Can't SSL on Socket");
}
}

public void handShake(String username, String password) throws IOException
{
credBuf = new byte[CREDBUFSIZ];

outUser = username.getBytes(hostCharSet);
System.arraycopy(outUser, 0, credBuf, 0, outUser.length);

outPwd = password.getBytes(hostCharSet);
System.arraycopy(outPwd, 0, credBuf, USERSIZ, outPwd.length);

out.write(credBuf, 0, CREDBUFSIZ);
out.flush();

if (in.read(t3IdBuf) < t3IdBuf.length)
{
System.out.println("Read < " + Integer.toString(t3IdBuf.length) + "
bytes");
throw new IOException();
}

inMsg = new String(t3IdBuf, 0, 3, hostCharSet);

if (!inMsg.equals(T3ID))
{
throw new IOException();
}
}

public void sendUrgentData (int oob) throws IOException
{
t3Sock.sendUrgentData(oob);
}

public void setTimeout(int msecs) throws UnknownHostException, IOException
{
t3Sock.setSoTimeout(msecs);
}

public void close () throws IOException
{
if (t3Sock != null && !t3Sock.isClosed())
{
try {t3Sock.close();}
catch (Exception e)
{e.printStackTrace();}
}
}

public void buffMessage (String message) throws IOException
{
byte [] msg = message.getBytes(hostCharSet);

out.write(msg);
}

public void sendMessage (String message) throws IOException
{
byte [] msg = message.getBytes(hostCharSet);

out.write(msg);
flush();
}

public void flush () throws IOException
{
out.flush();
}

public int readMessage () throws IOException
{
return readMessage(readBuf.length);
}

public int readMessage (int bytes) throws IOException
{
try
{
bytesIn = in.read(readBuf, 0, bytes);
}
catch (SocketTimeoutException e)
{
return 0;
}

return bytesIn;
}

public String getString () throws ArrayIndexOutOfBoundsException
{
return getString(0, bytesIn);
}

public String getString (int offset, int length) throws
ArrayIndexOutOfBoundsException
{
if ((offset + length) > bytesIn)
{
throw new ArrayIndexOutOfBoundsException();
}
try
{
stringOut = new String(readBuf, offset, length, hostCharSet);
}
catch (Exception e)
{
return null;
}

return stringOut;
}

}
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Richard said:
I have created a class that uses the java.net.Socket class to talk to my
server and everything is great. I then converted the code to use the
javax.net.SSLSocket class and (thanks to how easy Java makes it for us!)
everything is still great. What I want to do now is parameterize/optionalize
the use of SSL or in-the-clear Sockets within my class, and I'm struggling
to find a modular, let alone elegant, solution.

At the moment I plan to add another Applet parameter called SSL_REQD and I
will pass that to my constructor, but because of Java's compile-time
resolution of methods-to-objects, I find myself having to duplicate code
that is erstwhile 99% identical or common. Is there some way (short of
Reflection) that I can leverage the fact SSLSocket class inherits most of
its methods from the Socket class so that I only need one method for each
socket function regardless of what flavour socket is in use?

For example: -

private someSocket t3Sock;

if (sssReqd)
t3Sock = (SSLSocket)sockFactory.createSocket();
else
t3Sock = new Socket();

t3Sock.setKeepAlive(true);

Am I stuck with "One's an Apple and the other's an Orange (albeit painted
red :)"?

If few of the Socket methods are overridden by SSLSocket (and the
value-added encryption stuff happens at a lower/other level) can I just cast
my way around some of this? Just stick in a few "if" statements and stop
moanin'?

Since SSLSocket inherits from Socket then you can make your
t3sock of type Socket (you can assign from a subtype to a
super type).

If you need to use a SSL specific funtion you can use:
((SSLSocket)t3Sock).someSSLSOcketSpecificMethod()

But that is not "nice".

I would find it tempting to create a wrapper class hirachy:
CommonSocketWrapper, SocketWrapper and SSLSocketWrapper where
SocketWrapper has some do nothing methods for the SSL specific
stuff.

If that is not what you are looking for, then pleas explain.

Arne
 
R

Richard Maher

Hi Arne,

Thanks for the reply.

In the rest of this I'm not arguing against what you've said, I'm just
honestly trying to digest it
Since SSLSocket inherits from Socket then you can make your
t3sock of type Socket (you can assign from a subtype to a
super type).

But because SSLSocket "extends" Socket, surely I have to instantiate a
SSLSocket object somewhere don't I?

Really no need for this: -
sockFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
t3Sock = (SSLSocket)sockFactory.createSocket();

What about all the Value-Added SSL bits that the SSLSocket class must
bolt-on to a Socket?
If you need to use a SSL specific funtion you can use:
((SSLSocket)t3Sock).someSSLSOcketSpecificMethod()

You're casting t3Sock (a Socket object) as a SSLSocket object there right? I
haven't missed some abstract-class or Interface wizardry? So I've got a
vanilla Socket and cast it as a SSLSocket to call, say startHandshake(), and
it's not gonna complain about a bodgy brick-veneer job, absent any
certificate or crypto-algorithm info?

I'd find it easier to picture it the other way around where we have a
SSLSocket and our casting it as a Socket effectively masks out all the SSL
bits, but who cares? I'll just code it like you've said and see how I get
on.
But that is not "nice".

It's certainly more appealing than the attached code (look for "sslReqd")

I guess the least I can do is put the common stuff such as this. . .
inSock.setKeepAlive(true);
inSock.setReuseAddress(true);
inSock.setTcpNoDelay(true);
inSock.connect(new InetSocketAddress(host,port), CONTIMOUT);

in = new BufferedInputStream (inSock.getInputStream() ,maxBufSiz);
out = new BufferedOutputStream (inSock.getOutputStream(),maxBufSiz);

.. . . in a private method with a Socket parameter "inSock" and cast the
SSLSocket to that (or vica versa).

Cheers Richard Maher

import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.net.SocketTimeoutException;
import java.lang.System;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class Tier3Socket
{
public static final
String T3ID="T3$";
public static final
int USERSIZ=40;
public static final
int T3IDBUFSIZ=48;
public static final
int CREDBUFSIZ=80;
public static final
int CONTIMOUT=3000;

public byte [] t3IdBuf;
public byte [] readBuf;
public byte [] writeBuf;

private String host;
private int port;
private int maxBufSiz;
private int bytesIn;
private String hostCharSet;
private Socket t3Sock;
private SSLSocket t3SSLSock;
private SSLSocketFactory sockFactory;
private BufferedInputStream in;
private BufferedOutputStream out;
private byte [] outUser;
private byte [] outPwd;
private byte [] credBuf;
private String inMsg;
private String stringOut;
private boolean sslReqd;

Tier3Socket (String host, int port, int maxBufSiz, String hostCharSet,
boolean sslReqd)
{
this.host = host;
this.port = port;
this.maxBufSiz = maxBufSiz;
this.hostCharSet = hostCharSet;
this.bytesIn = 0;
this.sslReqd = sslReqd;

t3IdBuf = new byte[T3IDBUFSIZ];
readBuf = new byte[maxBufSiz];

if (sslReqd)
sockFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
}

public void open() throws UnknownHostException, IOException
{
if (sslReqd)
{
t3SSLSock = (SSLSocket)sockFactory.createSocket();
t3SSLSock.setKeepAlive(true);
t3SSLSock.setReuseAddress(true);
t3SSLSock.setTcpNoDelay(true);
t3SSLSock.connect(new InetSocketAddress(host,port), CONTIMOUT);

in = new BufferedInputStream (t3SSLSock.getInputStream() ,maxBufSiz);
out = new BufferedOutputStream (t3SSLSock.getOutputStream(),maxBufSiz);

t3SSLSock.setUseClientMode(true);

try {t3SSLSock.startHandshake();}
catch (IOException e)
{
System.out.println("Failed SSL Handshake");
throw new IOException("Can't SSL on Socket");
}
}
else
{
t3Sock = new Socket();
t3Sock.setKeepAlive(true);
t3Sock.setReuseAddress(true);
t3Sock.setTcpNoDelay(true);
t3Sock.connect(new InetSocketAddress(host,port), CONTIMOUT);

in = new BufferedInputStream (t3Sock.getInputStream() ,maxBufSiz);
out = new BufferedOutputStream (t3Sock.getOutputStream(),maxBufSiz);
}
}

public void handShake(String username, String password) throws IOException
{
credBuf = new byte[CREDBUFSIZ];

outUser = username.getBytes(hostCharSet);
System.arraycopy(outUser, 0, credBuf, 0, outUser.length);

outPwd = password.getBytes(hostCharSet);
System.arraycopy(outPwd, 0, credBuf, USERSIZ, outPwd.length);

out.write(credBuf, 0, CREDBUFSIZ);
out.flush();

if (in.read(t3IdBuf) < t3IdBuf.length)
{
System.out.println("Read < " + Integer.toString(t3IdBuf.length) + "
bytes");
throw new IOException();
}

inMsg = new String(t3IdBuf, 0, 3, hostCharSet);

if (!inMsg.equals(T3ID))
{
throw new IOException();
}
}

public void sendUrgentData (int oob) throws IOException
{
if (sslReqd)
t3SSLSock.sendUrgentData(oob);
else
t3Sock.sendUrgentData(oob);
}

public void setTimeout(int msecs) throws UnknownHostException, IOException
{
if (sslReqd)
t3SSLSock.setSoTimeout(msecs);
else
t3Sock.setSoTimeout(msecs);
}

public void close () throws IOException
{
if (t3Sock != null && !t3Sock.isClosed())
{
try {t3Sock.close();}
catch (Exception e)
{e.printStackTrace();}
}

if (t3SSLSock != null && !t3SSLSock.isClosed())
{
try {t3SSLSock.close();}
catch (Exception e)
{e.printStackTrace();}
}
}

public void buffMessage (String message) throws IOException
{
byte [] msg = message.getBytes(hostCharSet);

out.write(msg);
}

public void sendMessage (String message) throws IOException
{
byte [] msg = message.getBytes(hostCharSet);

out.write(msg);
flush();
}

public void flush () throws IOException
{
out.flush();
}

public int readMessage () throws IOException
{
return readMessage(readBuf.length);
}

public int readMessage (int bytes) throws IOException
{
try
{
bytesIn = in.read(readBuf, 0, bytes);
}
catch (SocketTimeoutException e)
{
return 0;
}

return bytesIn;
}

public String getString () throws ArrayIndexOutOfBoundsException
{
return getString(0, bytesIn);
}

public String getString (int offset, int length) throws
ArrayIndexOutOfBoundsException
{
if ((offset + length) > bytesIn)
{
throw new ArrayIndexOutOfBoundsException();
}
try
{
stringOut = new String(readBuf, offset, length, hostCharSet);
}
catch (Exception e)
{
return null;
}

return stringOut;
}

}
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Richard said:
But because SSLSocket "extends" Socket, surely I have to instantiate a
SSLSocket object somewhere don't I?

Really no need for this: -
sockFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
t3Sock = (SSLSocket)sockFactory.createSocket();

That works fine if t3Sock is declared to have type Socket.

And since all methods are virtual in Java, then if t2Sock is
a SSLSocket, then that class's methods will be used.
What about all the Value-Added SSL bits that the SSLSocket class must
bolt-on to a Socket?


You're casting t3Sock (a Socket object) as a SSLSocket object there right? I
haven't missed some abstract-class or Interface wizardry? So I've got a
vanilla Socket and cast it as a SSLSocket to call, say startHandshake(), and
it's not gonna complain about a bodgy brick-veneer job, absent any
certificate or crypto-algorithm info?

t3Sock is declared as a Socket, but it it actually is a SSLSocket, then
it can be cast to a SSLSocket and therefore use the SSLSocket
specific methods.
I'd find it easier to picture it the other way around where we have a
SSLSocket and our casting it as a Socket effectively masks out all the SSL
bits, but who cares?

You can not assign a Socket to a SSLSocket, so it has to be this way
around.
It's certainly more appealing than the attached code (look for "sslReqd")

True.

Arne
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Richard said:
But because SSLSocket "extends" Socket, surely I have to instantiate a
SSLSocket object somewhere don't I?

Really no need for this: -
sockFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
t3Sock = (SSLSocket)sockFactory.createSocket();

What about all the Value-Added SSL bits that the SSLSocket class must
bolt-on to a Socket?


You're casting t3Sock (a Socket object) as a SSLSocket object there right? I
haven't missed some abstract-class or Interface wizardry? So I've got a
vanilla Socket and cast it as a SSLSocket to call, say startHandshake(), and
it's not gonna complain about a bodgy brick-veneer job, absent any
certificate or crypto-algorithm info?

I'd find it easier to picture it the other way around where we have a
SSLSocket and our casting it as a Socket effectively masks out all the SSL
bits, but who cares? I'll just code it like you've said and see how I get
on.

Below are a little standalone console app client-server example.

I know you are in applet environment, but the idea is general.

Look for the hack section.

Arne

============================================================

import java.io.*;
import java.net.*;

import javax.net.ssl.*;

public class MultiServer {
private final static int PORT = 12345;
public static void main(String[] args) throws Exception {
ServerSocket ss;
if(args.length > 0 && args[0].equals("SSL")) {
System.out.println("SSL");
ss =
SSLServerSocketFactory.getDefault().createServerSocket(PORT);
} else {
System.out.println("Non-SSL");
ss = new ServerSocket(PORT);
}
System.out.println("Accepting connection");
Socket s = ss.accept();
System.out.println("Reading");
InputStream is = s.getInputStream();
byte[] b = new byte[10000];
int blen = 0;
int n;
while((n = is.read(b, blen, b.length - blen)) > 0) {
blen += n;
}
System.out.print("Received " + blen + " bytes from client:");
for(int i = 0; i < blen; i++) System.out.print(" " + b);
System.out.println();
System.out.println("Closing");
is.close();
s.close();
ss.close();
}
}

import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.*;

import javax.net.ssl.*;

public class MultiClient {
private final static String HOST = "localhost";
private final static int PORT = 12345;
public static void main(String[] args) throws Exception {
Socket s;
if(args.length > 0 && args[0].equals("SSL")) {
System.out.println("Connecting via SSL");
SSLContext sslctx = SSLContext.getInstance("SSL");
sslctx.init(null, new X509TrustManager[] { new
MyTrustManager() }, null);
SSLSocketFactory sf = sslctx.getSocketFactory();
s = sf.createSocket(new Socket(HOST, PORT), HOST, PORT, true);
} else {
System.out.println("Connecting plain");
s = new Socket(HOST, PORT);
}
// hack
if(s instanceof SSLSocket) {
SSLSession ses = ((SSLSocket)s).getSession();
System.out.println("Server: " + ses.getPeerPrincipal());
}
System.out.println("Sending 1 2 3 to server");
OutputStream os = s.getOutputStream();
byte[] b = { 1, 2, 3 };
os.write(b);
System.out.println("Closing");
os.close();
s.close();
}
}

class MyTrustManager implements X509TrustManager
{
public void checkClientTrusted(X509Certificate[] chain, String
authType) {
}
public void checkServerTrusted(X509Certificate[] chain, String
authType) {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}

C:\>java MultiServer
Non-SSL
Accepting connection
Reading
Received 3 bytes from client: 1 2 3
Closing

C:\>java -Djavax.net.ssl.keyStore=server.jks
-Djavax.net.ssl.keyStorePassword=superhemmeligt MultiServer SSL
SSL
Accepting connection
Reading
Received 3 bytes from client: 1 2 3
Closing

C:\>java MultiClient
Connecting plain
Sending 1 2 3 to server
Closing

C:\>java MultiClient SSL
Connecting via SSL
Server: CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
Sending 1 2 3 to server
Closing

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,734
Latest member
manin

Latest Threads

Top