User Tools

Site Tools


middleware:devel:guidelines

Middleware Development Guidelines and Practices

Subversion

Middleware uses Subversion for its code repository.
Developers should conform to the following directory structure for all coding projects:

--projname
  --branches
  --tags
  --trunk

All coding should be done in trunk, code will be branched and tagged for each release.
This isn't a hard and fast rule and exceptions can be made, however it appears to be the best practice for Middleware.
See the subversion section for example commands.

ED Project Layout

In order to ease development and deployment of ED projects, all ED projects should contain the file structure shown below.
This structure can be checked out of subversion, it has the module name sample.

bin\ any executable files (binaries, scripts) needed or provided for this project
checkstyle\ checkstyle jar(s) and configuration files
common\ jar(s) needed at compile time, but not at run time (includes jboss jars, servlet api, commons-logging, etc)
docs\ any documentation or relevant files used to create the project (requirements, analysis, design, testing documents)
etc\ any configuration files (web.xml, application.xml), it should not be necessary to modify them in order to build the project
lib\ jar(s) included with the project and needed at run time
properties\ any and all configuration files, including build properties
src\ source code and any static files which do not need to be modified for the project to run (i.e. truststores)
test-src\ source code used for testing this project, it should not be needed at compile time or run time (i.e. junit code)
web\ web pages, such as html or jsp files including all images and style sheets
LICENSE file which includes the license for the project (GPL, LGPL, BSD)
README file which includes a brief description of the project, how it is configured, what its dependencies are, how to deploy it, how to test it, and the current version of the project
build.xml ant build file

Coding Practices

This is not meant to be a comprehensive list of best practices, rather just the required practices.
Middleware encourages developers to learn more about best practices and continually improve their coding style.

Checkstyle

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard.
Checkstyle integrates with the ant build process so that builds fail if your source code does not pass the checks you have configured.
This allows the developer to enforce their coding style on a project when other developers need to edit their source.
It can also makes source code vastly more reabable, as coding conventions are the same throughout.
More details about checkstyle configuration can be found at their website, and a default checkstyle config is included in the sample subversion project.

Logging

if (LOG.isDebugEnabled()) {
  LOG.debug("User entered: "+input);
}
  • In general debug messages should not include the stack trace and error messages should. (Use your judgement.)
try {
  ...
} catch (NamingException e) {
  // this exception is common, write debug message
  if (LOG.isDebugEnabled()) {
    LOG.debug("Could not lookup name "+name);
  }
}

OR

try {
  ...
} catch (NamingException e) {
  // this exception shouldn't occur, write error message
  if (LOG.isErrorEnabled()) {
    LOG.error("Could not lookup name "+name, e);
  }
}
  • When catching an exception and then throwing an exception, always write an error message.
try {
  ...
} catch (IOException e) {
  if (LOG.isErrorEnabled()) {
    LOG.error("Could not open file "+filename, e);
  }
  throw new CouldNotOpenFileException(e);
}
  • More logging is always better than less.

General

  • Never use hard tabs
  • Break all lines at 80 characters
  • Javadoc all methods and class variables
  • Adhere to proper naming conventions
  • All built jar and sar files should include a version number, war and ear files should not.

Unit Testing

Unit testing with JUnit is as simple as writing some simple test classes, annotating which classes are test methods, and adding assertions to the test methods to ensure the supplied result matches the expected. JUnit can be integrated neatly with Ant to perform unit testing during the build process to ensure that software is defect-free before release. The following code samples show how to create some simple unit tests and integrate them into an existing Ant build process.

Environment Setup

The following discussion focuses on JUnit 4.x, which uses Java 5 annotations to define test methods and behavior. There are some difficulties with integrating JUnit 4.x with Ant 1.6.x, so Ant 1.7.x is strongly recommended. The following discussion and samples assume JUnit 4.x and Ant 1.7.x.

Sample Unit Test

Some notes about the following example:

  • All annotated test methods must be public
  • The @Before annotation flags a method to be executed before each test method. (@After annotation is similar.)
  • The @Test annotation flags a test method
  • See this developerWorks article for a more thorough dicussion of JUnit test scenarios and additional annotations.
package edu.vt.middleware.cas;
 
import java.util.List;
import java.util.Map;
 
import edu.vt.middleware.cas.authentication.EdAttributeFetcher;
 
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
 
/**
 * Tests the EdAttributeFetcher class.
 *
 * @author Marvin Addison
 * @version $Revision
 *
 */
public class EdAttributeFetcherTest {
 
    /** UID of test user */
    private static final String TEST_UID = "1145718";
 
    /** PID of test user */
    private static final String TEST_PID = "serac";
 
    /** Affiliations of test user */
    private static final String[] TEST_AFFILIATIONS = new String[] {
        "VT-ALUM",
        "VT-ALUM-CONSTITUENT",
        "VT-ACTIVE-MEMBER",
        "VT-EMPLOYEE",
        "VT-EMPLOYEE-STATE",
        "VT-STUDENT",
        "VT-STAFF",
    };
 
    /** The name of the EdAttributeFetcher bean */
    private static final String FETCHER_BEAN = "edAttributeFetcher";
 
    /** Reference to XML Spring application context */
    private static final ApplicationContext FS_CTXT =
        new FileSystemXmlApplicationContext(
            new String[]{ "resource/deployerConfigContext-test.xml", });
 
    /** Array of DNs of test groups to which test user belongs */
    private static final String[] TEST_GROUPS = new String[] {
        "uugid=ctu.admin,ou=Groups,dc=vt,dc=edu",
    };
 
    /** Fetches attributes from ED-ID */
    private EdAttributeFetcher fetcher;
 
    /**
     * Unit test bootstrap called before each test.
     */
    @Before
    public void setUp() {
        this.fetcher = (EdAttributeFetcher)FS_CTXT.getBean(FETCHER_BEAN);
    }
 
    /**
     * Tests fetching attributes via PID lookup.
     */
    @Test
    public void fetchByPid() {
        verifyAttributes(this.fetcher.fetchByPid(TEST_PID));
    }
 
    /**
     * Tests fetching attributes via UID lookup.
     */
    @Test
    public void fetchByUid() {
        verifyAttributes(this.fetcher.fetchByUid(TEST_UID));
    }
 
    /**
     * Verifies fetched attributes match expected values.
     * @param m Map of attribute name/value pairs.
     */
    private void verifyAttributes(final Map m) {
        Assert.assertEquals(this.fetcher.getAttributes().length, m.size());
        Assert.assertEquals(TEST_UID, m.get("uid"));
        Assert.assertEquals(TEST_PID, m.get("uupid"));
        Assert.assertTrue(contains(
                (List)m.get("eduPersonAffiliation"),
                TEST_AFFILIATIONS));
        Assert.assertTrue(contains(
                (List)m.get("groupMembership"),
                TEST_GROUPS));
    }
 
    /**
     * Determines whether each string in the contained argument is a member
     * of the container argument.
     * @param container List to search for contents.
     * @param contained String array of cotents to search for.
     * @return True if every member of contained exists in container, false
     * otherwise.
     */
    private boolean contains(final List container,
            final String[] contained) {
 
        if (contained.length > container.size()) {
            return false;
        }
 
        boolean found = false;
        for (int i = 0; i < contained.length; i++) {
            found = false;
            for (final Object item : container) {
                if (contained[i].equals(item)) {
                    found = true;
                }
            }
            if (!found) {
                return false;
            }
        }
        return true;
    }
}

Ant Integration

It is considered best practice to integrate unit testing into the software build process to help reduce software defects. The following Ant tasks should be added to an existing compile or build target.

<!-- Compile test source -->
<property name="test.class.dir" value="${build.dir}/testclasses" />
<mkdir dir="${test.class.dir}" />
<javac
	fork="yes"
	debug="yes"
	deprecation="no"
	optimize="no"
	destdir="${test.class.dir}"
	srcdir="test-src">
	<classpath>
		<path refid="build.classpath" />
		<pathelement location="${class.dir}" />
	</classpath>
</javac>
 
<!-- Run unit tests and fail build on failure -->
<junit printsummary="true" fork="true" forkmode="perBatch"
	failureproperty="unit.failure">
	<classpath>
		<path refid="build.classpath" />
		<pathelement location="${class.dir}" />
		<pathelement location="${test.class.dir}" />
	</classpath>
	<formatter type="plain" />
	<batchtest fork="true" todir="${report.dir}">
		<fileset dir="test-src">
			<include name="**/*.java" />
		</fileset>
	</batchtest>
</junit>
<fail if="unit.failure">Unit testing failed.
See JUnit reports in "${report.dir}" directory for details.</fail>
 
<!-- Clean up - delete compiled test classes -->
<delete dir="${test.class.dir}" />

The above code assumes you have created a ${report.dir} as part of build initialization.

middleware/devel/guidelines.txt · Last modified: 2015/06/01 12:02 (external edit)