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.
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 |
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 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.
if (LOG.isDebugEnabled()) { LOG.debug("User entered: "+input); }
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); } }
try { ... } catch (IOException e) { if (LOG.isErrorEnabled()) { LOG.error("Could not open file "+filename, e); } throw new CouldNotOpenFileException(e); }
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.
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.
Some notes about the following example:
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; } }
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.