Novell is now a part of Micro Focus

Java Meets Novell's GroupWise API Gateway, Part I

Articles and Tips: article

ASHLEY JOHNSON
Senior Consultant
Consulting Services

AL YOUNG
Senior Research Engineer
Developer Information

DAYLE S WOOLSTON
Senior Software Engineer
Advanced Development Group

01 May 1997


The first in a series of articles explaining how to enable Java applets and applications to create files that can serve as GroupWise messages, Part I covers the Development Context, the Object Model, and the Message Class.

Introduction

In this article, we begin a discussion of how to enable Java applets and applications to create files that can serve as GroupWise messages. The article series assumes a readership consisting of application developers that may be new to object-oriented analysis and design, as well as being somewhat new to Java.

Development Context

Figure 1 illustrates a context in which the kind of Java classes presented in this discussion might be used with The GroupWise Gateway API.

Figure 1: Context for using the sample Java classes.

Files produced by our Java classes are placed on a server whose directory structure is scanned by the Gateway. For an explanation of that directory structure, and other details concerning the handling of files, etc., see the Aunderstand directory structure and message flow" heading in the GroupWise Gateway API Help. GroupWise API Gateway documentation, describing format requirements for files produced by the kind of Java classes presented in this article series, is available to members of Novell's DeveloperNet Program at:

http://151.155.48.82/Grpwise/Docs/APIGATE.HTM

The gateway itself is available for download via a link from the same location.

A more specific example of the use that can be made of the sample Java classes appears in Figure 2.

Figure 2: A more focused context for using the sample Java classes.

The context presented in Figure 2 might pertain to an organization in which information stored in a relational or object database must be disseminated to a variety of destinations. For example, a company might have numerous customers or partners interested in being notified that information in a particular database has been updated. If the interested parties cannot access the database, E-mail may be the best means of keeping them informed. Our sample Java classes could be used to put the information into a form whereby the GroupWise Gateway can distribute it. Thus, this article series expands on one aspect of the "Hypothetical publishing environment" presented in a previous Developer Notes article entitled "An Object-Oriented Approach to Automated Information Mark-Up".1 We also refer the reader to "Building GroupWise 5 Gateways Using the API," which appeared in the February 1997 edition of Developer Notes.2

Outline of Article Series

The Java classes, introduced in this article, will be discussed in a four-part article series in which each article will focus on one of the following topics:

  • Message format

  • Parsing

  • Validation and exception handling

  • Integration

Message format is the focus of this first article in the series. Accordingly, we address only the definition and creation of classes used to model the message that is part of our application domain. Parsing involves reading data formatted according to the GroupWise Gateway API specifications. Validation and exception handling pertain to ensuring the integrity of both content and format, and building behaviors that are as robust as possible. Finally, the article on integration will focus on how to incorporate sample classes into either a Java application or applet so that the software can take full advantage of functionality available via Novell's GroupWise Gateway API.

Object Model

As discussed in previous Developer Notes articles about object-oriented application development3, the design and development of such applications usually involves formulating what is referred to as an object model.4 Accordingly, we begin with an object model of the GroupWise message.

The purpose of this model is to enable us to construct Java classes capable of conforming to the file format requirements of the GroupWise Gateway API.5 Simply stated, we want to create Java applets and/or applications capable of reading and writing files of the format appearing in Figure 3. One of the first steps in doing so is to design an object model that abstracts not only the characteristics of such a file, but some of the behaviors to be associated with such files.

Figure 3: Sample GroupWise file.

WPC-API= 1.2; 

Header-Char= T50; 

Msg-Type= MAIL; 

From-Text= admin; 

From= MERCURY1.NCS.admin;

To= Mercury.Mercury1(Updater);

All-To= Mercury.Mercury1(Updater);

Msg-Id= 321C92E2.125F.0001.000;

To-Text= Mercury.Mercury1.Updater;

Subject= Postman test 1; 

Date-Sent= 22/8/96 11:03;

Security= Normal; 

Send-Options= Notify-User;

Status-Request= Delivered, Opened; 

Msg-Priority= Normal;

Attach-File= 

    Current-File= 321c3e83;

    Original-File= AUTOEXEC.BAT;

    Size= 246; 

    Date= 27/7/96 11:47; ,

    Current-File= 321c3e84;

    Original-File= AUTOEXEC.OLD;

    Size= 45; 

    Date= 26/10/95 4:13; ,

    Current-File= 321c3e85;

    Original-File= HELLOTST.NLM;

    Size= 780; 

    Date= 16/8/96 4:37; 

    ;   

-END-

In Figure 4, we present part of the design documentation that results from our attempt to build an object model (i.e., Figure 4 presents part of the design documentation we wind up with). Most of this article talks about how we manage to start with the kind of file in Figure 3 and wind up with the kind of design appearing in Figure 4 (as well as other design documents presented in this article).

Figure 4 shows inheritance 6 among classes used to represent the fields in a GroupWise message. The gateway's view of messages defines the basic outline of our object model because we want to communicate with that gateway. In naming the classes presented in this article, we use the term "model" to indicate an abstract class.

Figure 4: Abbreviated class diagram of the Message and ContentComponentModel hierarchies.

Diagrams of the kind presented in Figure 4 are enormously important in visualizing the architecture imposed upon an application domain. Without such diagrams, designers, developers and others must build their own representations piecemeal as they struggle to understand both the domain and the architecture applied to it. Diagrammatic representations facilitate communication among team members. This is particularly important as the size and complexity of application domains continues to increase.7 Design and development efforts benefit because more of the architecture can be seen, evaluated, and improved. And, finally, maintenance and enhancement (or re-development) phases benefit because members new to a team are likely to have a shorter learning curve, and members who have been on the team all along can have at least some hope of remembering what they did and maybe even why they did it.

A Note About Class Hierarchies

The design and implementation of class hierarchies has long been discussed in the literature of object technology. It is outside the scope of this article to enumerate qualitative criteria for creating such hierarchies. It is also without the purview of this article to provide a step-by-step guide for constructing hierarchies. Nevertheless, we believe that at least one consideration in designing such hierarchies ought to be remarked.

In building an object model of virtually anything, it is important to keep in mind the distinction between how an object is created (e.g., how an object comes to be constructed within the resulting software), and the use made of that object within the same software. Whether or not this distinction seems subtle, it is absolutely pivotal whenever class architecture is considered.

Inheritance should model how an object is created (e.g., how a particular object gets its variables, gets its methods). Inheritance should not model relationships in the same way. For example, if we were building an object model of a flashlight, the batteries would not be a subclass of flashlight just because the flashlight contains batteries. In our model for a flashlight, batteries would probably inherit from Object just like the Flashlight class. Each instance of Flashlight would, of course, have a variable containing instances of the Battery class, but Battery would not inherit from Flashlight because the attributes and behaviors of the two classes diverge too greatly to generate instances of them in the same manner.8 This consideration relates to the decision concerning whether a role played by an entity in an application domain should be modeled as a class.9

We bring this up at this point in the discussion in order to provide some explanation for the nature of the object model presented in Figure 4. Otherwise, examining such a model may not reveal the principles that shape it. It is true that OT attempts to model the "real world"; nevertheless, such modeling involves much more than merely looking for those objects in that world that appear relevant to the applet or application to be developed.10 As remarked in previous Developer Notes articles, object orientation requires an object-oriented mind set.

Java Meets Novell's GroupWise API Gateway, Part I

Message Class

In Code Listing 1, we begin an examination of message.java, the source file from which the Message class is defined. Numbers prefixed to each line have been added for this article, and are not, of course, part of actual code.


Note: Code listings in this article do not contain validation and exception-handling behaviors requisite to the complete and proper functioning of the sample classes. Only aspects of class definition and object creation appear in the versions of sample classespresented in this article's code listings. Validation and exception-handling will be discussed in a subsequent article.

Code Listing 1

1: import java.io.File;

2: class Message {

3: protected File file;

4: protected WpcApi wpcApi;

5: protected HeaderCharacter headerCharacter;

6: protected MessageType messageType;

7: protected FromText fromText;

8: protected From from;

9: protected To to;

10:     protected AllTo allTo;

11:     protected ToCc toCc;

12:     protected AllToCc allToCc;

13:     protected ToBc toBc;

14:     protected MessageId messageId;

15:     protected MessageFile messageFile;

16:     protected ToText toText;

17:     protected ToCcText toCcText;

18:     protected Subject subject;

19:     protected DateSent dateSent;

20:     protected Security security;

21:     protected SendOptions sendOptions;

22:     protected StatusRequest statusRequest;

23:     protected MessagePriority messagePriority;

24:     protected AttachedFile attachedFile;

25:     protected End end;

At the outset of message.java, the import statement on line 1 makes a basic Java class available to this definition of the Message class. Such statements do not somehow bring the specified classes into the code, but enable the developer to use a kind of shorthand designation in referring to classes.11 (This is similar to #include in C++.) Thus, the reference to File on line 3, for example, can be made simply by using the name of the File class instead of specifying java.io.File.

Line 2 begins the definition of the Message class, which inherits from Object. (Because "extends Object" does not appear as part of line 2, object is implied as the immediate superclass for Message.)

The variables to be assigned to each instance of a Message are defined in lines 3 - 25. Variables in the list are the variables belonging to each instance of Message, not to the Message class itself. Hence, whenever a message is created (i.e., whenever an instance of the Message class is constructed), that instance will have the 23 variables defined in lines 3 - 25.

The definition of each message attribute specifies the visibility of the attribute. In this case, each attribute is protected.12 The class of the object contained in the variable is then declared. For example, the file attribute defined on line 3 must contain an instance of the class File, the messageId attribute defined on line 14 must contain an instance of the MessageId class, and so on.

Finally, each attribute definition in Code Listing 1 specifies the name of the attribute. Thus, the message file, when sent to a message, results in the message returning to the requestor that instance of File assigned to the receiver's file variable. The file variable holds onto the file that our Java application places in the GroupWise Gateway directory structure for distribution (see figures 1 and 2).

File is a base class in the standard JDK. Its functionality is adequate for our purposes, and so we use it without extending or otherwise modifying the class. However, the objects assigned to the other variables belonging to an instance of Message are instances of classes that are not part of the standard JDK. Because of the requirements of our particular application domain, we've had to extend the class hierarchy provided as the basis for programming in Java.

In addition to learning how to analyze and then decompose a domain into objects, the OO developer must be familiar enough with base classes (regardless of the tool or implementation environment) to know when to use existing classes and when to extend them. This knowledge is at the heart of object-oriented programming.

For example, we might have assigned each Message variable an instance of the String class (e.g., the subject variable might have contained an instance of String, as might each of the 21 other variables -- excluding the file variable, which must contain an instance of File). Such an implementation would be adequate only insofar as the functionality of the object in a message variable need be only as functional as an instance of String would be. But if we want to have objects in those variables that can respond to a request for a keyword, for example, required by the GroupWise Gateway API, we may want to consider creating an object that holds on to a String and also knows how to respond to a request for a keyword. And as our investigation of the application domain continues, we may find it desirable to have those same objects able to respond to a request to constitute themselves from data in an input stream.

Such is the case in our approach to this particular application domain, and to understand the reasons for the architecture presented in Figure 4, let's begin with the inheritance hierarchy presented in Figure 5.

Figure 5: Class Diagram of inheritance for Subject.

The definition of StringModel, the parent class of Subject, appears in Code Listing 2. In that listing we see that StringModel adds two behaviors to its class hierarchy:

write() parse()

Additionally, StringModel adds to its class hierarchy only one variable:

value

In the value variable (see Code Listing 2, line 3), each instance of a subclass of StringModel will hold on to an instance of String. This variable, therefore, contains what we consider the text, or content, of the field in an E-mail message. Thus, when an instance of StringModel (i.e., any subclass of StringModel) is asked for its value, the requestor is given that instance of the String class assigned to the receiver's value variable.

The two methods that StringModel adds to the hierarchy enable any instance of a StringModel to respond to a request to either write() or parse() what is handed to these methods along with the request. For example, the write() method anticipates that an instance of PrintStream will be handed to the method so that a StringModel can use the stream for output. The parse() method performs an analogous, but opposite, function.

Code Listing 2

1.   import java.io.PrintStream;

2.   abstract class StringModel extends AssignmentModel {

3.     protected String value;

4.     public void write(PrintStream anOutputStream) {

5.        super.write(anOutputStream);

6.        anOutputStream.print(value);

7.     }

8.     void parse(GwapiTokenInputStream anInputStream) {

9.        super.parse(anInputStream);

10.       value = anInputStream.getValue();

11.    }

12.  }

These are the classes in our proposed hierarchy that are subclasses of StringModel:

StringModel

  FileSize

  FromText

  HeaderCharacter

  MessageId

  OriginalFile

  SendOptions

  StatusRequest

  Subject

  ToCcText

  ToText

WpcApi

To understand why these classes are grouped as subclasses of StringModel, and other classes in the hierarchy are not, let's look at the definition of DateModel, provided in Code Listing 3.

Code Listing 3

1:   import java.io.InputStream;

2:   import java.io.PrintStream;

3:   import java.io.IOException;

4:   import java.util.Date;

5:   import java.util.StringTokenizer;



6:   abstract class DateModel extends AssignmentModel {

7:     Date value;

8:     DateModel() {

9:        value = new Date();

10:    }

11:    void parse(GwapiTokenInputStream anInputStream) {

12:       super.parse(anInputStream);

13:       String dateString = anInputStream.getValue();

14:       StringTokenizer tokens = new StringTokenizer(dateString, " :/", false);

15:       String tmp = tokens.nextToken();    //gets day of the month

16:       value.setDate(Integer.valueOf(tmp).intValue());

17:       tmp = tokens.nextToken();

18:       value.setMonth(Integer.valueOf(tmp).intValue()-1);

19:       tmp = tokens.nextToken();

20:       value.setYear(Integer.valueOf(tmp).intValue());

21:       tmp = tokens.nextToken();

22:       value.setHours(Integer.valueOf(tmp).intValue());

23:       tmp = tokens.nextToken();

24:       value.setMinutes(Integer.valueOf(tmp).intValue());

25:       value.setSeconds(0);

26:    }

27:    void write(PrintStream anOutputStream) {

28:       super.write(anOutputStream);

29:       anOutputStream.print(toString());

30:    }

31:    public String toString() {

32:       StringBuffer buf = new StringBuffer();

33:       buf.append(value.getDate());

34:       buf.append("/");

35:       buf.append(value.getMonth()+1);

36:       buf.append("/");

37:       buf.append(value.getYear());

38:       buf.append(" ");

39:       if (value.getHours() < 10)

40:         buf.append("0");

41:       buf.append(value.getHours());

42:       buf.append(":");

43:       if (value.getMinutes() < 10)

44:         buf.append("0");

45:       buf.append(value.getMinutes());

46:       return buf.toString();

47:    }

48:  }

From Code Listing 3, we see that like StringModel, DateModel adds write() and parse() behaviors to the class hierarchy.

The behavior provided by these methods differs significantly from the behavior of the methods with the same name in the StringModel hierarchy. Alone this difference justifies dividing the AssignmentModel hierarchy. Further examination of Code Listing 3 reveals that a constructor is provided for this abstract class so that when the class is asked for an instance of DateModel, the instance that is returned has a date assigned already to its value variable. Finally, the definition in Code Listing 3 provides DateModel with its own version of the toString() method.

In a cursory examination of code listings 2 and 3, we see that while the subject field of an E-mail message and any of the date fields of such a message each has content, the manner in which they provide that content differs widely. Inasmuch as either of the write() or parse() behaviors is assigned at object creation, the inheritance hierarchy must be divided accordingly. Of course it would be possible to provide a single construction capability that could test for the particular behavior to be assigned an object, but such a practice runs counter to reusability. The polymorphism of these objects is ensured by having instances respond to identical messages, while the class hierarchy is used to promote reusability by assigning appropriate behaviors to each object type.

Moving up the class hierarchy, we come to AssignmentModel, which is defined by the code appearing in Code Listing 4.

Code Listing 4

1:   import java.io.PrintStream;

2:   abstract class AssignmentModel extends ContentComponentModel {

3:     void write(PrintStream anOutputStream) {

4:        super.write(anOutputStream);

5:        anOutputStream.print("= ");

6:     }

7:     void parse(GwapiTokenInputStream anInputStream) {

8:        super.parse(anInputStream);

9:        int aCharacter = anInputStream.read();

10:    }

11:  }

In Code Listing 4, write() and parse() behaviors are again specified. This is important as super statements appear throughout this hierarchy to appropriately fragment these behaviors. Note, for example, that by having subclasses of AssignmentModel class use their superclass's version of write(), the behavior specific to the equal sign is found only in the write() method belonging to AssignmentModel. This kind of fragmentation of behavior across a hierarchy significantly enhances code maintainability and reusability. Other aspects of these behaviors, more appropriate to super- or subclasses is apportioned accordingly.

Finally, we come to ContentComponentModel, which inherits only from Object.

Code Listing 5

1:   import java.io.PrintStream;

2:   abstract class ContentComponentModel {

3:     protected static String keywordNameOn(int aToken) {

4:        return keywords[aToken].name;

5:     }

6:     protected static int nextValue = -1;

7:     public static final int SUBJECT_TOK = ++nextValue;

8:     public static final TokenNode[] keywords = {

9:        new TokenNode("Subject", new Integer(SUBJECT_TOK)),

10:    };

11:    void parse(GwapiTokenInputStream anInputStream) {

12:       int aToken = anInputStream.getKeyword();

13:    }

14:    abstract protected int token();

15:    void write(PrintStream anOutputStream) {

16:       write(anOutputStream, token());

17:    }

18:    static void write(PrintStream anOutputStream, int aToken) {

19:       anOutputStream.print(keywordNameOn(aToken));

20:    }

21:  }

In the definition of ContentComponentModel, the following variables are specified:

nextValue

SUBJECT_TOK

keywords

These behaviors are defined:

keywordNameOn()

parse(anInputStream)

token()

write(anOutputStream)

write(anOutputStream,aToken)

The nextValue variable belongs to the ContentComponentModel class (because it is declared as a static variable), and is used to automate the assignment of an integer to each node in the array of keywords.

The SUBJECT_TOK variable, also belonging to the ContentComponentModel class, is assigned an Integer by which the node belonging to that element of a GroupWise message can be looked up in the class's keywords array. Code Listing 5 includes only SUBJECT_TOK. An actual definition of the ContentComponentModel would include one such variable for each field of a message.

The keywords variable belongs to the ContentComponentModel class, and is used to store an array of the tokens belonging to each field in a GroupWise message.

The keywordNameOn() method is used by the ContentComponentModel class to look up the instance of TokenNode, in the class's keywords array, corresponding to the integer passed in with the keywordNameOn() method.

The parse() method, as defined in ContentComponentModel, retrieves only a field's keyword from the input stream. Earlier, we mentioned the importance of "factoring" behavior across the levels in a class hierarchy so that the behavior at a particular level corresponds to information (i.e., variables available) at each level in the hierarchy. In passing the execution of a behavior up and down a hierarchy, we propose a refinement of heuristic 2.9 in Riel's discussion of OO design heuristics.13 An examination of the hierarchy in Figure 5 reveals that the following classes in that hierarchy implement a parse(anInputStream) behavior.

ContentComponentModel

   AssignmentModel

StringModel

Thus, an instance of the Subject class (or any subclass of StringModel) responds to the parse(anInputStream) request as indicated in the sequence diagram presented in Figure 6. In Figure 6, the superclasses of Subject are represented by dashed-line versions of the diagrammatic icon for object.

Figure 6: Sequence diagram for parse(anInputStream) in StringModel hierarchy..

In the Date Model hierarchy, the sequence of events differs greatly from the behavior in StringModel classes. For example, DateModel classes do the same thing shown in Figure 6 for StringModel, except that once the result of getValue() is returned from anInputStream, DateModel objects use the value to modify the date assigned to them at creation. StringModel classes, on the other hand, simply assign the value returned from getValue(). (Compare code listings 2 and 3.) Such divergence of behavior is part of the reason behind the manner in which we've divided the AssignmentModel hierarchy.

ContentComponentModel and Message

The architecture described in this article suggests that Message be used to model a GroupWise message, and that the ContentComponentModel hierarchy be used to model message constituents. The reason for this is fundamentally that by having a containment relationship between a message and its fields, instead of having an inheritance relationship between them, fields can be added to or removed from a message with minimum consequences on the application.

The information and behavior associated with a message is isolated from (or independent of) the information and behavior belonging to any or all of the fields constituting a message. This kind of compartmentalization of software is a bright star in the constellation of reusability by which OT hopes to steer the industry.

References

  1. Young, Al; Johnson, Jay M. "An Object-Oriented Approach to Automated Information Mark-Up," Novell Developer Notes(vol. 3, no. 12), December 1996, p. 9.

  2. Di Bari, Bartolomeo. "Building GroupWise 5 Gateways Using the API," Novell Developer Notes(vol. 4 no. 2), February 1997, pp. 32-39.

  3. Young, Al; Woolston, Dayle S; Johnson, Jay M. "An Object-Oriented Approach to Modeling Information Content," Novell Developer Notes (vol. 3, no. 10), October 1996, pp. 22-56.

  4. Booch, Grady. Object-Oriented Analysis and Design with Applications, 2nd ed. Menlo Park, California: Addison-Wesley Publishing Company, 1994, pp. 27-80.

  5. See "header file syntax" in the list of help topics available in the GroupWise API Gateway Help.

  6. Unified Modeling Language Notation Guide version 1.0. Santa Clara, California: Rational Software Corporation, 1997, p. 51.

  7. Booch, Grady. "Why We Model," Object Magazine (vol. 6, no. 9), November 1996, pp. 79-80.

  8. McGregor, John D.; Korson, Tim. "Supporting dimensions of classification in object-oriented design," Journal of Object-Oriented Programming (vol. 5, no. 9), February 1993, pp. 25-30.

  9. Riel, Arthur J. Object-Oriented Design Heuristics. Reading, Massachusetts: Addison-Wesley Publishing Company, Inc., 1996, p. 24.

  10. Scharenberg, M. E.; Dunsmore, H. E. "Evolution of classes and objects during object-oriented design and programming," Journal of Object-Oriented Programming (vol. 3, no. 5), January 1991, pp. 30-34. See also Partridge, Chris. "Modelling the real world: Are classes abstractions or objects?" Journal of Object-Oriented Programming(vol. 7, no. 7), November-December 1994, pp. 39-45.

  11. Flanagan, David. Java in a Nutshell: A Desktop Quick Reference for Java Programmers. Sebastopol, California: O'Reilly & Associates, Inc., 1996, p. 19.

  12. Flanagan, David. Java in a Nutshell: A Desktop Quick Reference for Java Programmers. Sebastopol, California: O'Reilly & Associates, Inc., 1996, pp. 72-74.

  13. Riel, Arthur J. Object-Oriented Design Heuristics. Reading, Massachusetts: Addison-Wesley Publishing Company, Inc., 1996, p. 20.

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates