Page tree
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 13 Next »

Introduction

These guidelines gives some recommendations for those who want to adapt the current code and/or send in new plugins. They should be regarded as recommendations, not rules, but following them will make life easier for both parties.

Furthermore they give guidelines for how to adapt the current code to the design that are already decided and used in the NetarchiveSuite software. This relates to the design described in the System Design document.

Guidelines on Unit tests can be found in Old unit test guidelines.

Java Related

Coding style

Our overall coding style is based on Sun's guidelines with the following extensions:

IDE supported code styles

The general Java code style settings are defined in the ./eclipse-formatter-settings.xml file. This file can be imported by:

Eclipse: See Code Formatter Preferences.
IntelliJ:
 See IntelliJ IDEA 13: Importing Code Formatter Settings from Eclipse. Currently importorder is not imported, so these are maintain separatly in IntelliJ . IntelliJ can also export it codestyle, see Copying Code Style Settings.

Codestyles changes should be initiated in Eclipse and copied to other IDE's.

IDE defined code styles

  • Indent: We use 4 space indents, no tabs.
  • Line length is 120 chars.
  • Imports: All imports are declared explicitly, so no *. We use the following ordering (empty lines included):
    1. import static *
    2. import java.*
    3. import javax.*
    4. import all other
    5. import dk.netarkivet.*

Other code styles

Headers

We add the following header to all our Java-files. This is done by using the maven-license-plugin. All headers can be updated by running the license:update-file-header

mvn license:update-file-header

Or by running the ./precommit.sh script, which includes a step to update file heades.

Dependency injection

Classes should have all their external dependencies, including configurations, injected through the constructor. This allows the creator of the class to define the external dependencies, which is very relevant for testing purposes.

The dependencies should be access through provider classes allowing the creator to dynamically decide when instances should be created or reused. So a constructor example could be: 

public JobSupervisor(Provider<JobDAO> jobDaoProvider, Provider<Long> jobTimeoutProvider) {

Further reading:

Other guidelines

Nested class definitions

Declare nested classes as static whenever possible. This avoids an unnecessary link back to the outer class, and in particular makes it possible to serialized the inner class even if the outer class is not serializable. The "invisible" link to the outer class found in a non-static inner class can also lead to unexpected memory leaks, as an inner instance may outlive its outer instance and keep it artificially alive through its implicit link. Nested class definitions appear at the beginning of the enclosing class before (static) variables.

Example:

class A {
    public static B {
        // B stuff
    }
    public static Integer ACONST=42;
    ...
}

Variable declarations

The general rule is "put declarations only at the beginning of blocks". We allow one exception from this rule, namely declarations that 1) initialize the variable and 2) depend on previous calculations are allowed be further down the block. Example:

void Foo() {
int i1=42;
int i2=0;
i2 f(i1);
int i3 g(i2);
}

Miscellaneous

Public methods should always check that their arguments follow the JavaDoc restriction with respect to being null, empty, non-negative etc. The ArgumentNotValid class has a number of useful methods for this.

JavaDoc is strongly encouraged, as the code might explain what happens, but not the why; the JavaDoc must describe the intent of the function, including assumptions and invariants as well as expectations of the arguments.

Logging

We use the apache.commons.logging framework for logging (currently version 1.0.4), which gives us one unified interface that can be realized with different underlying systems.

However, currently the monitoring component requires the underlying implementation to be Jdk14 logging, since it exposes log messages using a LogHandler implementation for the Jdk14 framework. To use another logging framework, this method would need to be redefined for that framework (for instance an appender for Log4J).

Getting a logger

Retrieving an logger is a straightforward matter. Each class uses the logger interface to obtain a logger, using the class name to identify the logger.

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class MyClass {
   // A named logger for this class is retrieved
   protected final Log log LogFactory.getLog(getClass());
}

Using the logger

Logging messages are generated using the level specific logging methods:

  • log.fatal("Error situations where the application can not recover" ,Throwable);
  • log.error
  • log.warn("Error situations where the application may continue to operate" ,Throwable);
  • log.info("Information about major modules/functionalities. Like Application data loaded, Application services started. also used, where log.config() was used before" ,Throwable)
  • log.debug("Major functionalities within individual packages and classes are reported; Also used for tracing. Information within each method may be displayed" ,Throwable)
  • log.trace("Detailed tracing information" ,Throwable)

See http://jakarta.apache.org/commons/logging/guide.html#Logging_a_Message

When we used java.util.logging, we used these levels

  • logger.severe("Error situations where the application can not recover")
  • logger.warning("Error situations where the application may continue to operate")
  • logger.info("Information about major modules/functionalites. Like Application data loaded, Application services started")
  • logger.config("Only used to report application configuration information")
  • logger.fine("Major functionalities within indvidual packages and classes are reported")
  • logger.finer("Used for tracing. Information within each method may be displayed")
  • logger.finest("Detailed tracing information")
  • logger.log( FINER, "Exception logging", exception)
public class MyClass {
...
  public MyClass() {
    logger.trace("Creating MyClass");
    ...
    logger.info("Timeout="+timeout);
  }
  public void aMethod() {
    logger.trace("Entering aMethod");
    try {
      logger.debug("preparing to do something");
      doSomethingThatMayThrowExceptions();
    }
   catch (NullPointerException e) {
    logger.trace("something threw nullpointer exception", e);
    }
   catch (Exception e) {
    logger.trace("something threw an exception", e);
    }
    logger.trace("Leaving aMethod");
  }
}

Logger configuration

The logging levels and handlers are defined using a configuration filelog.prop=. The loglevels mentioned in this configuration file uses loglevels defined by java.util.logging.Level

An example file:

#define handler(s)
handlers=java.util.logging.FileHandler
#define default logging level
.level=INFO
#setup the file handler
java.util.logging.FileHandler.pattern=java%g.log
java.util.logging.FileHandler.limit=100000
java.util.logging.FileHandler.count=3
java.util.logging.FileHandler.formatter=java.util.logging.XMLFormatter
#define logging levels
dk.netarkivet.level=WARNING
dk.netarkivet.demo.MyClass.level=ALL

In order to load the configuration file add the following to the java command lines:

-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger
-Djava.util.logging.config.file=log.prop
  • No labels