Globus Toolkit: Coding Guidelines

Introduction

This guide provides coding guidelines for developing with the Globus Toolkit 4.0 and its components.

Java Coding Guidelines

In an attempt to increase code readability and hopefully productivity, the Globus Alliance requests that all programmers follow the standard defined below for programming in Java.

Base coding conventions

Use the Sun Coding Conventions for the Java Programming Language:

http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html

APIs/Classes

Internal APIs/classes should contain an "internal" component in their class path.

Statements

Import Statements

All imports must be single class and explicit.

In other words,

import <package>.* 

is not allowed.

Variables

No acronyms or abbreviations should be used.

For example,

 a = b + mVarLen 

should be avoided.

Use this instead:

totalLength = partLength + newLength

Instance Variables

Use this. as the prefix when referencing instance variables.

For example,

public MyClass (ServicePropertiesInterface properties) {
     this.properties = properties;
}

public int foo () {
    int localInt = 3;
    return this.instanceInt + localInt;
}

Instance variables should never be declared public. If instance variables need to be accessed from outside of the class then use getters/setters.

We leave it to the implementers' discretion to choose between protected and private as appropriate.

Layout

Indentation

All indentation levels should be four (4) spaces.

No editor tabs are allowed unless they are converted to four spaces before saving the file.

Brackets

Two models are allowed.

Important: They must never be mixed within the same source file and should not be mixed within the same package:

  1. Curly brackets {} are put on separate lines. E.g.
  2. for (index = 0; index < length; index++)
    {
         <code>
    }
  3. As defined in the Java Coding guidelines. E.g.
  4. for (index = 0; index < length; index++) {
         <code>
    }

One-Liners

Even single line statements should be inside brackets. E.g.

if (isEmpty) {
     return true;
}

Input

Configuration files

Information in configuration files should be dealt with the i18n way (e.g. path names or subject DNs containing multi-byte characters). Using XML based config files makes this easy.

Command line interfaces

Client command line interfaces should be internationalized.

Output

Internationalization: What

The rule of thumb is: All messages exposed to clients must be translatable. The following table describes in detail what must be translatable and what does not need to be translatable:

Message
Translatable
Errors (All error message strings exposed to clients)
YES
Logging (All log messages with filter ERROR or WARNING)
YES
Logging (All other log messages such as TRACE and DEBUG)
NO
Samples/Tests (Messages in samples and tests)
NO
Experimental/Prototype code
NO

Internationalization: How

Use the following to manage internationalization:

Never construct sentences from parts of sentences or words from word fragments.

Sorting, searching and merging of string structures (arrays, lists etc) must be i18n-enabled.

Dates and numbers

Dates and number formats must be internalized

Logging

The Jakarta Commons API should be used exclusively.

Log4j.properties should be used for configuring logs.

System.out/err.println is not allowed.

The default logging level for the Java container is 'info', which means 'info', 'warn', 'error', and 'fatal' messages are being shown.

It is important to ensure that log message are appropriate in content and severity. The following guidelines are suggested:

FATAL Severe errors that cause premature termination.
ERROR Other runtime errors or unexpected conditions.
WARN Poor use of API, 'almost' errors, other runtime situations that are undesirable or unexpected, but not necessarily "wrong". Examples of Globus errors in this category are problems verifying a user proxy or the reason a job failed. Messages should only be one-liners (no stack traces or xml dumps)
INFO Interesting runtime events. Be conservative and keep to a only useful information, such as when a user is authenticated or when a resource starts a major task. Messages should only be one-liners (no stack traces or xml dumps)
DEBUG Detailed information on the flow through the system. This level is to be used when the container needs to be debugged and any information which the developer might find useful can be outputted. Note that you should not have to turn on debug in order to understand for example why a job failed. Messages like that should be presented at for example the 'warn' level.
TRACE More detailed information.

Testing

Each component/class should have a JUnit test.

The tests should be put in the test/ directory under each package directory.

Library Reuse

Treat all code as a library, and as a reusable component.

Calls to System.exit() are not allowed (except the main method.)

Exceptions

The base class for remote exceptions is org.oasis.wsrf.faults.BaseFaultType. Services should throw this exception instead of java.rmi.RemoteException.

If the exceptions should be exposed to remote clients, define it in WSDL and extend the BaseFaultType defined in share/schema/wsrf/faults/WS-BaseFaults.xsd.

C Coding Guidelines

In an attempt to increase code readability and productivity, the Globus Alliance requests that all programmers follow the standard defined below for programming in C.

Code Block Style

  • Opening braces should be placed on a line by themselves and indented to the same column as the previous statement. 
  • Closing braces should also be placed on a line by themselves and indented to the same column as the matching opening brace.
  • The beginning of each statement should be indented four spaces more than the previous open brace.
  • Line length should be restricted to 80 columns.
  • Continuation lines should be indented.

Variable Declarations

Variables should be declared using the follow format:

[/* comment for one or more related variables;     comments that
     overflow a single should be left justified with the start
     of the comment text */]
 <type>                                   <var name>;


Each variable declaration should appear on its own line, and all variable names and comments should be aligned.  If the variable being declared is a pointer, the asterisk(s) should be placed with the <type> rather than with the <var name>.

In general, variables should declared such that their scope is minimized.  Global and module specific (static) variables should be avoided whenever possible.  If data needs to be shared across functions (and handlers), space for it should be allocated on the heap and registered as thread specific data. This helps avoid common multithreading bugs.  In the case of data being shared between a handler and a function which initiated that handler (either directly or indirectly) and is now waiting for that handler to complete, the function may allocate space for the data on the current thread's stack prior to initiating the send_rsr.  It is however essential that the handler desist from accessing those data after the function returns.

If necessary, module specific and global variables may be created.  Module specific variables should declared at the top of the globus_<package>_<module>.c file.   Global variables, like module specific variables, should be declared at the top code file, but should also be declared external (extern) in the globus_<package>_<module>.h file.

Variable names should follow a format of:

globus{,_i,_l}_<package>[_<module>]_<subject>

thus disclosing the hierarchy to which the variable belongs, while also avoiding symbol name space pollution (i.e., all global symbols are named globus_*).  In two cases, the name should be augmented with an 'i' or an 'l' (ell):

  • Variables which are for internal development use only, but which are symbols in the global namespace, should have an 'i' prepended to the package name.
  • Variables which are module specific (local, file static variables) should have an 'l' prepended to the package name.

Function Definitions

All functions should be specified using the following format:

  /**
  * <brief descriptive sentence>
  * <longer description of function>
  *
  * @ingroup <globus_<package>_<module>_<doxygen group>>
  *
  * @param <param1>
  *        <description of param1 with each line of the
  *        description indented to line up with param1>
  * ...
  * @param <paramN>
  *        <description of paramN>
  *
  * @retval <value1>
  *         <description of return value value1>
  * ...
  * @retval <valueN>
  *         <description of return value valueN>
  */
 [static]
 <return type>
 <function name>(
     <type1>                       <param1>,
     <type2>                       <param2>,
     ...
     <typeN>                       <paramN>)
 {
     <code block(s)>
 }
 /* <function name>() */

To elaborate, this means that the type of the value returned by this function should be placed on a separate line from the function name.  Additionally, each parameter declaration should be listed on a separate line and indented to column 4.  In addition, the parameter names should be aligned at a standard tab stop, preferably columns 40.  Function names should follow a format of

globus{_i,_l}_<package>[_<module>]_<subject>_<action>

thus disclosing the hierarchy to which the function belongs.  In two cases, the package field should be augmented with an 'i' or an 'l'.  Functions which are for internal development use only should have an 'i' prepended to the package name.  Functions which are module specific (local) should have an 'l' prepended to the package name.

The documentation comments for a function should reference a group id with an @ingroup tag.  To define a group that we refer to here, see the section below

Here's an example:

  /**
  * Cancel a particular job.
  * This function removes a PENDING job request or kills
  * all processes associated with an ACTIVE job.
  * 
  * @ingroup globus_gram_client_job
  *
  * @param job_contact
  *        identifier of the job
  *
  * @retval GLOBUS_SUCCESS
  *         the operation completed successfully 
  * @retval GLOBUS_GRAM_CLIENT_ERROR_*
  *         an error occurred *
  * @see globus_gram_client_job_request()
  */
 int
 globus_gram_client_job_cancel(
     char *                              job_contact)
 {
     /* a set of related variables used for some purpose */
     int *                               variable1;
     globus_bool_t                       variable2;
   
     /* another variable for some other purpose */
     float                               variable3;
   
     /*
      * Function body goes here
      */
   
     return(GLOBUS_SUCCESS);
 }
 /* globus_gram_client_job_cancel() */


Functions should be declared as module specific (with a type of static) whenever possible. Module specific functions should have a prototype declaration registered at the top of the module's code file. Functions which are part of an end-user API must be declared in an installable header file (globus_<package>_<module>.h). All other functions should be declared in an internal header file (globus_i_<package>_<module>.h).

Typedefs and Structures

Typedefs and structures should follow the same symbol naming conventions as variables and functions, with that addition that all structure names should be appended with "_s", and all typedefs should be appended with "_t":

globus{_i,_l}_<package>[_<module>]_<subject>_{s,t}

The documentation comments for a structure  should reference a group id with an @ingroup tag.  To define a group that we refer to here, see the section below

For example:

  /**
  * @struct globus_gram_client_list_t
  *
  * Brief structure definition.
  * Long structure description that talks about how
  * this is used, possibly including references to
  * other related stuff.
  *
  * @ingroup <globus_<package>_<module>_<doxygen group>>
  *
  * [@see globus_gram_client_list_add_member()]
  */
 typedef struct globus_gram_client_list_s
 {
     void *                              value;
     struct globus_gram_client_list_s *  next;
 }
 globus_gram_client_list_t;

Preprocessor Directives

Some compiler implementers have interpreted the ANSI standards as requiring a # preprocessing token as the first character of a line of a preprocessor directive.  For this reason it's recommended that if you want to include indentation in preprocessor directives that you do so between # and the keyword token. 

For example:

#ifdef USE_TRACEPOINT
#    include "tracepoint.h"
#else
#    define tracepoint(str)
#endif

Mainpage and Group Documentation

Include a long description for each Globus module in the main header file of a Globus module. 

As an example, the long description (or mainpage) should look something like:

/**
  * @mainpage
  *
  * The Globus Replica Catalog C API provides operations
  * to manage the information in a replica catalog.
  * There are functions and structures for
  * @link globus_replica_catalog_connection
  * managing connections @endlink
  * to a collection in a replica catalog, as well as for
  * managing
  * @link globus_replica_catalog_collection
  * collection @endlink,
  * @link globus_replica_catalog_location
  * location @endlink, and
  * @link globus_replica_catalog_logicalfile
  * logical file @endlink
  * information. Each of these last three contain
  * functions that add, delete, and query their respective
  * information.
  *
  * At a high level, a @e replica @e catalog maps logical
  * filenames to physical locations...
  */

Also, include at least one Doxygen group definition block for each logical grouping of functions and structures within a Globus module.  It should have an id, title, and description of the form:

/**
  * @defgroup <id> <title>
  * <brief descriptive sentence>
  * <longer group description>
  */

The id is used to reference this group and should have the form:

 globus_<package>_<module>_<doxygen     group>

A reference to a group should be included in every function and structure documentation comment (with @ingroup <id>). 

As an example, a group definition should look something like:

/**
  * @defgroup globus_replica_catalog_connection Connection Management
  *
  * Manage a connection to a collection in a replica catalog.
  *
  * As an example let's configure and open a connection:
  * @code
  * globus_replica_catalog_handleattr_t handleattr;
  * globus_replica_catalog_handle_t handle;
  * globus_replica_catalog_handleattr_init(&handle);
  * globus_replica_catalog_handleattr_set_auth_mode(
  *     &handleattr,
  *     GLOBUS_REPLICA_CATALOG_AUTH_MODE_CLEARTEXT,
  *     directory_manager_dn,
  *     directory_manager_passwd);
  * globus_replica_catalog_open(
  *     &handle,
  *     &handleattr,
  *     ldap_contact_url);
  * @endcode
  */

(The above example uses abbreviated function names that don't necessarily reflect actual Globus function names.)

See the Doxygen site for more information about how to use tags (@commands) in your documentation comments. 

Miscellaneous

Do not abbreviate.  Abbreviations may be convenient for you, and you may know what the abbreviation stands for.  However, they may not be obvious to others, which will hinder the maintainability of the code.