Generics and Method Objects

Generics and Method Objects

Adding Generic Exceptions to the Command Object Framework

As I mentioned above, it would be absolutely great to be able to use a type variable to indicate a variable length number of exceptions. Unfortunately, this is not possible in the current generics specification. The best we can do is to use a single type variable, which is usually E, and then use as narrow a class as possible when creating instances. Let's look once more at AbstractRemoteMethodCall



    public abstract class AbstractRemoteMethodCall<T, E extends Exception> {
        public T makeCall() throws ServerUnavailable, E {
        // ...
        }

        protected abstract T performRemoteCall(Remote remoteObject) throws RemoteException, E;
        // ...
    }

In TranslateWord, we narrow T and E as follows:

    public class TranslateWord extends ServerDescriptionBasedRemoteMethodCall<Word, CouldNotTranslateException>> {
        private Word _sourceWord;
        private Language _targetLanguage;
        public TranslateWord(ServerDescription serverDescription, Word sourceWord, Language targetLanguage) {
            super(serverDescription);
            _sourceWord = sourceWord;
            _targetLanguage = targetLanguage;
        }

        protected Word performRemoteCall(Remote remoteObject) throws RemoteException, CouldNotTranslateException {
            Translator translator = (Translator) remoteObject;
            return translator.translate(_sourceWord, _targetLanguage);
        }
    }

And then, in ClientFrame, TranslateWord is used in a single place, inside a single try/catch block:

            try {
                Word result = translateMethod.makeCall();
                resultText = result.toString();
            }
            catch (CouldNotTranslateException CNTE) {
                resultText = COULD_NOT_TRANSLATE_STRING;
            }
            catch (ServerUnavailable  SUA) {
                resultText = SERVER_UNAVAILABLE_STRING ;
            }
/*
    the old version of Translate word had to catch a generic exception here
            catch (Exception e) {
                resultText = e.toString();
            }
*/

            finally {
                _resultsPanel.setText(resultText);
            }

The point of this is that invocation to makeCall only needs to catch the exceptions that are actually thrown by makeCall. Using a type variable lets us narrow the range of thrown exceptions: the superclass' ServerUnavailable and E become E becomes CouldNotTranslateException in TranslateWord. The value of eliminating catch (Exception e) should not be underestimated.

There is one final wrinkle to consider: suppose the method on the server doesn't throw any exceptions (other than RemoteException). What should E be declared as? Well, one trick, which I'm simultaneously proud to have come up with and ashamed to use, is to bind the type variable E to RuntimeException, as in the following definition of GetRegistryContents.

    public class GetRegistryContents extends AbstractRemoteMethodCall<Collection<String>, RemoteException> {
        //...

        protected Collection<String> performRemoteCall(Remote remoteObject) throws RemoteException {
        // ...
        }

        // ...
    }

This actually works! RuntimeException is a subclass of Exception and so the compiler is happy (recall that E was defined to extend Exception). At the same time, RuntimeException explicitly does not need to be caught, and so the client code doesn't wind up having extraneous catch blocks. Indeed, here's the code from TranslatorPanel that uses GetRegistryContents:

        GetRegistryContents getRegistryContents = new GetRegistryContents (_registryServerName, _registryPort);
        try {
            Collection<String> serverNames = getRegistryContents.makeCall();
            model.setContents(serverNames);
        }
        catch (ServerUnavailable logged) {
            ExceptionLog.reportException(logged);
            showRemoteErrorMessage();
        }

Note: Another interesting facet of GetRegistryContents is that the type variable T is set to Collection<String>. Generics can be used recursively.

Summary

This article is the last one (for now) in this series. In the first article, I introduced a command object framework to encapsulate remote method calls in RMI. In the second article, I extended the command object framework to seamlessly implement a local stub cache behind the scenes. In doing so, I also removed the lookup code from various places in the client, instead putting most of it into a new abstract command object, ServerDescriptionBasedRemoteMethodCall. And at this point, the cost-benefit analysis looks like the following:

Pro

  • Encapsulation is good. Encapsulating method calls in separate objects makes the main program logic easier to read. ClientFrame is easy-to-read code and all the details of of the remote call have been placed in a separate location.

  • ServerDescription is logically clean. We've now put the naming and lookup code in a single place and replaced calls to the RMI registry with creating ServerDescription objects. Remote method invocations now consist of creating a server description and passing it to a command object.

  • Latency has been reduced. Stubs are fetched once and then cached locally.

  • Difficult logic is implemented once, in the framework. The retry logic is implemented once, in an abstract base class, and is correct.

  • The client code is simple. The code required to create a new subclass of ServerDescriptionBasedRemoteMethodCall is almost trivial -- it's both easy to write and easy to see (at a glance) that it's correct.

  • The framework provides hooks. We have some very nice hooks for using different retry strategies based on context. We also have some very nice hooks for inserting logging functionality.

Con

  • Indirection is confusing. Extra classes that encapsulate requests and the attendant level of indirection can be confusing. For small applications, using command objects can feel like overkill.

It's a long list of benefits versus a single complaint. And, just between us, I don't think the complaint is all that justified.

William Grosso is a coauthor of Java Enterprise Best Practices.


Also in this series:

Learning Command Objects and RMI -- O'Reilly's Java RMI author William Grosso introduces you to the basic ideas behind command objects by providing a translation service from a remote server and using command objects to structure the RMI made from a client program.

Seamlessly Caching Stubs for Improved Performance -- In Part 2 of this RMI series, William Grosso addresses a common problem with RMI apps -- too many remote method calls to a naming service. In this article he extends the framework introduced in Part 1 to provide seamless caching of stubs.


Return to ONJava.com.

Prev  [1] [2] [3] [4] 

Close    To Top
  • Prev Article-Java:
  • Next Article-Java:
  • Now: Tutorial for Web and Software Design > Java > Java Basic > Java Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction