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
The coding style was decided in 2014 to be the default Eclipse formatter setting (as of 4.4) with the following additions:
- Line width set to 120 characters (as Java is considered a bit too verbose for the default to work well)
- Indentation is 4 spaces. No tabs.
The exported settings can be found in the builds-tools/src/main/ressources dir.
Source can be formatted by maven using
mvn com.googlecode.maven-java-formatter-plugin:maven-java-formatter-plugin:format
Note: In order for IntelliJ and Eclipse to format sources interchangeably the import sorting order must be the same. For simplicity we went with the Eclipse defaults.
All imports are declared explicitly unless there is more than 99 classes used in a package (so no * by default) - this has the advantage of being unambigious. We use the following ordering (empty lines included between each category):
- import static *
- import gnu.*
- import java.*
- import javax.*
- import org.*
- import com.*
- import all other
Codestyles changes should be initiated in Eclipse and copied to other IDE's.
Eclipse:
- Use File->Import->General->Preferences to import eclipse-xml-settings.epf in build-tools/src/main/resources
- Use Preferences->Java->Code Style->Formatter->[Import] to import eclipse-formatter-settings.xml in build-tools/src/main/resources
(README.txt in the root Maven project contains the latest instructions)
IDEA IntelliJ:
- See IntelliJ IDEA 13: Importing Code Formatter Settings from Eclipse for instructions on how to use build-tools/src/main/resource/eclipse-xml-settings.epf.
- The following settings needs manual setup, as these aren't imported from Eclipse:
- Code styles -> General: Right margin (columns) needs to set to 121 to avoid wrapping 120 length lines
- Java
- Tabs and indents:
- Continuation indent should be 8.
- Javadoc:
- Do not wrap on line javadoc comment needs to be enabled
- Preserve line feeds
- Tabs and indents:
- Import statement
- Order must be manually defined, see specification above
- Import statement
- Xml
- Tabs and Indents: Set continuation indent to 2
- Other: Disable Align attributes.
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
NetarchiveSuite was designed in another age with a lot of central methods elsewhere being called to provide a given service. It has since been learned that quality improves if it is possible to test individual components on their own without having the full system available, and a good way to do that in Java is to provide external dependencies explicitly in the class constructor, as it allows for providing exactly what is needed in the given situation.
When conversion is complete, it may be realistic to use a dependency injection framework to make this easier to manage.
An example:
public JobSupervisor(Provider<JobDAO> jobDaoProvider, Provider<Long> jobTimeoutProvider) {
The Provider<X> interface provides a get() method returning X. This allows for lazy initialization and/or providing an unknown number of X'es.
Further reading:
- http://www.vogella.com/tutorials/DependencyInjection/article.html
- http://square.github.io/dagger/
- http://www.infoq.com/presentations/Dagger
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. Please add and improve as needed.