Whoa, I was just trying to fix the System.exit() problem :)

I agree with you that there is a bit too many responsibilities for one object, and that parsing of arguments should be separated from execution of logic. However, I don't think that I should attack the separation of establishing a remote connection and driving the wire protocol as part of this bug. I think we should log a separate bug for that issue, I think it's a great thought for a more flexible design.

For this bug, I could use it as an opportunity to take all command-line parsing *out* of DB2jServerImpl, that makes a lot of sense. DB2jServerImpl should only be called by NetworkServerControl anyway, and should present a more classic object abstraction and not have anything to do with argument parsing. BTW, it was my mistake to use DB2jServerImpl in the example below, it should have been NetworkServerControl.

In this particular example, NetworkServerControl also has methods for each command, and the Derby docs explicitly document that this is how you work with the network server in embedded mode. So an embedding app shouldn't ever have to or need to call main() or any routine that just takes an array of args, and doesn't need the model I propose below.

However, other Derby tools, however, like ij and dblook, don't have this command-based interface. They do need some kind of "execute" method that matches the way it is invoked from the command-line. As discussed, if the behavior of exit status when returning from main() is well defined by the Java spec, then we can just use main with no call to System.exit(). Otherwise, we should provide a separate execute() method for embedding applications.

If we decide upon a separate execute() method, my question still is, what is the easier way to invoke this execute method, using an arg array, or by instantiating an instance and setting bean properties? I think this is an indepedent question from any separation of concern issues within the tool itself. I personally think an arg array is easier, but perhaps I'm missing an important architectural issue.

Thanks,

David

Jeremy Boynes wrote:

David Van Couvering wrote:


Also, the JavaBean approach assumes you create an instance of the beast, and I have noticed that many command-line classes have most of their code in static methods and use static variables (not that they should, but that's what they do). Also, it might actually be easier to set up an array of Strings rather than call a bunch of set methods, especially if you can create the array in-place, e.g.


DB2jServerImpl.main(new String[] { "ping", "-p", "2000"} );

rather than

DB2jServerImpl impl = new DB2jServerImpl()
impl.setCommand("ping");
impl.setPort("2000");
impl.execute();


My concern here is coming from confusion in the responsibilities between NetworkServerControl and DB2jServerImpl. For example, there are two ways to set the maxthreads:


1) new NetworkServerControl().setMaxThreads(100);

2) NetworkServerControl.main(
     new String[]{"maxthreads", "100"});

Ultimately these both end up calling DB2jServerImpl.netSetMaxThreads()

DB2jServerImpl is doing three completely different things here: it is setting up the remote connection, it is driving the wire protocol to the remote server (see the impl of netSetMaxThreads), and it is responsible for parsing and verifying the command line arguments passed in. I think these concerns can be separated resulting in a simpler implementation that is easier to embed and use from other tools such as Eclipse.

I would first suggest factoring out the connection setup and teardown - perhaps a NetworkServerConnection class with hostname and port properties (perhaps username, password or other credential) and an execute method that takes a command object e.g.

public class NetworkServerConnection {
   public void connect() throws TransportException;
   public void close() throws TransportException;

   public Object execute(NetworkServerCommand cmd)
      throws TransportException, CommandException;
}

Each command (ping, maxthreads, etc) is represented by an object that implements NetworkServerCommand and which can be set up with the appropriate properties e.g.

public class SetMaxThreadsCommand implements NetworkServerCommand {
   public void setMaxThreads(int threads);
}

Each command would know how to write itself to the wire; heck if you were prepared to break backward compatibility you could simplify things further by just using serialization.

For command line support, NetworkServerControl.main parses the arguments, creates a NetworkServerConnection and the appropriate command and then submits it to the remote server. Basically it just contains implementation for handling the command line syntax.

For embedded use, a tool can create and maintain a connection for as long as necessary, getting the connection parameters any way it wants (e.g. a host/user/password dialog). The commands can be created and submitted in response to UI events just like any others. Both the connection and the commands can use beaninfo to define their properties.

Although there are more moving parts in this, I think overall it is a more maintainable solution as the concerns of connection, commands and UI have been clearly separated.

--
Jeremy

Reply via email to