---- '''Note that the information has being migrated to the new [[https://sbforge.org/display/NAS/NetarchiveSuite|SBForge wiki]]''' ---- <> <> <> <> = 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 [[Guidelines/Unit Test Guideline|the unit test guidelines]] = Java Related = == Coding style == Our overall coding style is based on [[http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html|Sun's guidelines]] with the following extensions: === File headers === we add the following header to all our Java-files: {{{ /* File: $Id: DBConnect.java 837 2009-06-09 15:13:41Z ngiraud $ * Revision: $Revision: 837 $ * Author: $Author: ngiraud $ * Date: $Date: 2009-06-09 17:13:41 +0200 (ti, 09 jun 2009) $ * * The Netarchive Suite - Software to harvest and preserve websites * Copyright 2004-2007 Det Kongelige Bibliotek and Statsbiblioteket, Denmark * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ }}} and the following header to our JSP-files: {{{ <%-- File: $Id: Bitpreservation-filestatus-missing.jsp 781 2009-03-25 13:39:20Z elzi $ Revision: $Revision: 781 $ Author: $Author: elzi $ Date: $Date: 2009-03-25 14:39:20 +0100 (on, 25 mar 2009) $ The Netarchive Suite - Software to harvest and preserve websites Copyright 2004-2007 Det Kongelige Bibliotek and Statsbiblioteket, Denmark This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA --%> }}} === SVN keywords === If you commit a new file directly to our SVN repository, you must add the SVN property (svn:keywords) for this file to the value: {{{ URL Revision Author Date Id }}} Eclipse will add this automatically for you for all new files if you make sure that auto-props are enabled for subversion. The following modifications to the file $HOME/.subversion/config needs to be made Add or uncomment the line {{{ enable-auto-props = yes }}} Add the following to the end of the file: {{{ [auto-props] *.java = svn:keywords=URL Revision Author Date Id *.xml = svn:keywords=URL Revision Author Date Id *.jsp = svn:keywords=URL Revision Author Date Id *.css = svn:keywords=URL Revision Author Date Id *.properties = svn:keywords=URL Revision Author Date Id }}} === Tabs === Remember that tabs are not wanted in our code. You must tell your IDE (Eclipse or Intellij) to replace all tabs with spaces. === Unit Tests === All code must be covered as much as possible in unit tests. Please refer to [[Guidelines/Unit Test Guideline|unit test guideline]] for more information. === 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 === Don't use * import statements, it clutters up the namespace and makes it hard to see what is intended. Good IDEs can do your imports automatically anyway. Tabs should never be used in the source files. Most editors can use spaces instead. 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. == Exceptions == At the outset of the project, we decided to use undeclared exceptions throughout our code to avoid to avoid cluttering method definitions with exceptions that are merely passing through, and to have more flexibility in what exceptions can be thrown in subclasses and interface implementations. Before you argue this decision, please read the [[http://www-106.ibm.com/developerworks/java/library/j-jtp05254.html|arguments]] [[http://www.mindview.net/Books/TIJ/|for]] and [[http://www.artima.com/intv/solid.html|against]]. Notice that the fact that an exception is unchecked does not mean that you don't need to document its usage in !JavaDoc for your methods, and you can still add it to the throws clause. At any point where exceptions enter our code, we catch them and either handle them immediately or throw one of our exceptions instead, with the caught exception as the cause. All our exceptions inherit from ~+`dk.netarkivet.common.exceptions.NetarkivetException`+~, and we try to keep the number of exceptions at a minimum. At the moment, the following exceptions exist: * dk.netarkivet.common.exceptions.!PermissionDenied - used when user rights are not sufficient to perform an operation, or authentication has failed. * dk.netarkivet.common.exceptions.UnknownID - used when trying to look up an item that does not exist.. * dk.netarkivet.common.exceptions.IOFailure - used for a plethora of unpredictable file-system or network failures, when no better cause (like !PermissionDenied) can be ascertained. * dk.netarkivet.common.exceptions.!ForwardedToErrorPage - used solely in JSP page support code to abort operations after forwarding to an error page. This should be caught in the JSP page and processing of the JSP stopped. * dk.netarkivet.common.exceptions.!ArgumentNotValid - used in all public methods for checking basic validity of arguments. Covers and provides methods for checking errors like passing null references or empty strings. Should not be used to indicate things like missing files. * dk.netarkivet.common.exceptions.!IllegalState - used when something can be in one of several states, and an operation is performed that is not appropriate for the current state. * dk.netarkivet.common.exceptions.!NotImplementedException - used as a placeholder in methods that are not implemented, or in a few system-specific places for instance trying to get the number of bytes free on a disk when running on a system that doesn't have that function implemented. One standard example of how to catch outside exceptions and handle resource freeing is: {{{ InputStream in; try { try { in = new FileInputStream(file); in.readAll(...); } finally { if (in != null) { in.close(); } } } catch (IOException e) { throw new IOFailure("Failed to read file '" + file + "'", e); } }}} Notice how the error message contains the file name in quotes (makes it easier to understand empty file error), and how the ~+`IOFailure`+~ gets the original exception passed in -- it is very important to never let the original exception vanish. When it comes to handling our internal exceptions, the general rule is: Avoid catching exceptions unless you need to catch it. Also expressed as "Never catch an exception that you do not know how to handle" (with apologies to H. P. Lovecraft). You need to catch an internal exception if: * Resources must be released that cannot be properly releases with finally. Pay special attention to constructors. * Your code can fix the problem and try again * Your code must try an alternative execution strategy * Your are implementing a toplevel method like main() == Logging == We use the [[http://jakarta.apache.org/commons/logging/|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/functionalites. Like Application data loaded, Application services started. also used, where log.config() was used before" [,Throwable]) * log.debug("Major functionalities within indvidual 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 file =log.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 }}} = Design Related = '''''The following is to be split between the [System Design] document and this document, so that coding guidelines of how to code according to this design is put here, while description of how the design works is put into the [[System Design]] document.''''' <>