Guidelines for Contributing Code to the Archivists’ Toolkit Project

NOTE:

The guidelines below were posted prior to the decision to integrate the Archivists' Toolkit and Archon applications. Since that integration project is underway and is expected to release a next generation archives management tool, the AT team is no longer supporting the integration of branched AT code. Institutions that branch the AT code are strongly encouraged to document their branching and to share that documentation with the AT / Archon integration project.



These guidelines are intended to ensure that code contributed to the Archivists’ Toolkit (AT) application is of a high quality, works well, and is sufficiently documented for developers and intended users of the AT application.

Code not conformant to these guidelines will not be integrated into the main code trunk of the AT.

Please note that branched instances of the AT code that are not integrated into the main code are NOT the responsibility of the AT product team. The AT product team cannot assure that such branches can be considered in future versioning of the AT main code.

G1: Contributing Code

The Archivists’ Toolkit product accepts two basic kinds of contributions to the AT application:

  1. code for new or enhanced features / functions that is intended to be integrated into the AT main code trunk and be available to all users in subsequent releases of the application, and
  2. plugins, JasperReports, and XSLT stylesheets for enhanced features / functions, reports, exports which are not integrated into the code trunk and are optional for users.

Proposed changes to the AT core code are subject to review and approval. Until further notification, proposed changes should be submitted to the AT product team at info@archiviststoolkit.org.

Contributors should determine at the start of a development project if the contribution will be of benefit to most users and thus managed as code to be integrated into the AT core code or is of benefit to select users and thus managed as a plugin, JasperReport, or stylesheet.

It is strongly encouraged that plugins be used for modifications to the interface and functions of the AT application. Where necessary to support contributions, the AT product team will modify or extend the plugin architecture in the core code.

As a rule, the AT product team will accept changes to the data model that do not conflict with existing elements and features, and the team will work with code contributors to implement those changes in the core.

G2: Community reception

For contributed code to be integrated into the code trunk, evidence that the AT user community is receptive to the proposed features / functions should be presented. Evidence might typically take the form of 1) a detailed description / specification of the features / functions, which can be shared with the AT user community, and 2) reports / responses from members of the AT user community indicating support for the contributions

The purpose of requiring that prospective code contributions be shared as feature description / specification in advance of the contribution is to protect the application against non-useful feature bloat.

This requirement does not apply to plugins, JasperReports, or XSLT stylesheets.

G3: Code testing and debugging

Code contributed to the AT project is required to be thoroughly tested and debugged by the contributor before the code is integrated into the AT code trunk. Also, testing and debugging of code after integration into the main trunk will first be the responsibility of the contributor. The product team will then conduct subsequent testing and debugging of the integrated code before releasing an updated version of the AT. It will also be the responsibility of the contributor to fix all bugs reported by the AT product team that can be attributed to the new code and its integration.

G4: Code documentation

Code contributed to the AT code base should be sufficiently documented to make the intention of the code understandable to the community of AT developers. An example of code deemed to be sufficiently documented is provided at the end of these guidelines.

Remember that other developers may want to build on or modify contributed code. Good code documentation goes a long way to making code understandable and facilitating the community development process.

G5: User documentation

When contributing code to the AT, the contributor must also provide sufficient user documentation to be incorporated within or added to the overall AT user documentation. There is no hard rule for indicating what is sufficient. However, the current AT User Manual (http://www.archiviststoolkit.org/support/userManual1_1.shtml) provides a good example of documentation that covers what a feature is and how it is to be used. It also includes a broad range of indices, including validation tables, lists defining data types, and data maps.

The AT product team will be responsible for deciding how best to add contributed documentation to the AT User Manual. Some contributions will undoubtedly stand as discrete chapters, while other contributions will be incorporated into already extant chapters, appendices, or other documents.

G6: Copyright / License notices

The Archivists’ Toolkit is issued under the Educational Community License, version 1.0 (http://www.archiviststoolkit.org/downloads/license.shtml). Contributed code should be compatibly licensed.

G7: Distribution

Any distribution of core code prior to integration in the AT core code is the sole responsibility of the contributor. The AT product team will only distribute the contribution as an integration to a new release of the AT.

Plugins, JasperReports, and XSLT stylesheets, which are not integrated into the code base, can be posted to the Archivists Toolkit forumsfor downloading and use by interested users.

The Archivists’ Toolkit is a trademark. It is not permissible to distribute un-integrated branches of the AT code under the Archivists’ Toolkit name without the expressed permission of the AT product team.

G8: Support

While not a requirement for contributing code, plugins, JasperReports, or stylesheets, it is strongly encouraged that contributors identify themselves as the author of the contribution and provide contact information so that other AT users and developers can ask questions about the contribution(s) directly to its author(s).

Example Code with Documentation

1:package org.archiviststoolkit.plugin;
2:
3:import org.archiviststoolkit.model.ATPluginData;
4:import org.archiviststoolkit.model.validators.ATValidator;
5:import org.archiviststoolkit.model.validators.ValidatorFactory;
6:import org.archiviststoolkit.mydomain.DomainAccessObject;
7:import org.archiviststoolkit.mydomain.DomainAccessObjectFactory;
8:import org.archiviststoolkit.mydomain.DomainObject;
9:import org.archiviststoolkit.util.JGoodiesValidationUtils;
10:import com.thoughtworks.xstream.XStream;
11:import com.jgoodies.validation.ValidationResult;
12:
13:import java.util.Collection;
14:import java.awt.*;
15:
16:/**
32: * This is a utility class to make it easier for plugin developers to save
33: * data to the AT database and perform validation on AT records.
34: *
35: * @author: Nathan Stevens
36: * Date: Feb 11, 2009
37: * Time: 8:07:58 PM
38: */
39:public class ATPluginUtils {
40: /**
41: * Method to save text data to the database
42: *
43: * @param pluginName The name of the plugin
44: * @param dataVersion The dataVersion
45: * @param dataName The name of the data
46: * @param dataType The type of data
47: * @param dataString The text data to save
48: * @throws Exception is thrown if there was a problem saving the data
49: */
50: public static void saveData(String pluginName, int dataVersion,
51: String dataName, String dataType,
52: String dataString) throws Exception {
53: ATPluginData pluginData =
54: new ATPluginData(pluginName, false, dataVersion,
55: dataName, dataType, dataString);
56: saveToDatabase(pluginData);
57: }
58:
59: /**
60: * Method that first converts a java object to an xml string
61: * then save it to the database
62: *
63: * @param pluginName The name of the plugin
64: * @param dataVersion The dataVersion
65: * @param dataName The name of the data
66: * @param dataType The type of data
67: * @param dataObject The object that contains the data or is the data
68: * @throws Exception is thrown if there was a problem saving the data
69: */
70: public static void saveData(String pluginName, int dataVersion,
71: String dataName, String dataType,
72: Object dataObject) throws Exception {
73: // use Xstream to convert the java object to an xml string
74: XStream xstream = new XStream();
75: String dataString = xstream.toXML(dataObject);
76:
77: ATPluginData pluginData =
78: new ATPluginData(pluginName, true, dataVersion,
79: dataName, dataType, dataString);
80: saveToDatabase(pluginData);
81: }
82:
83: /**
84: * Saves plugin data object to the database
85: *
86: * @param pluginData The plugin data object
87: * @throws Exception If there is any problems saving to the database
88: */
89: public static void saveToDatabase(ATPluginData pluginData) throws Exception {
90: try {
91: DomainAccessObject access =
92:
DomainAccessObjectFactory.getInstance().getDomainAccessObject(ATPluginData.class);
93: access.getLongSession();
94: access.updateLongSession(pluginData);
95: } catch(Exception e) {
96: e.printStackTrace();
97: throw new Exception("Error Saving Plugin Data to Database ...");
98: }
99: }
100:
101: /**
102: * Method to delete plugin data in the database
103: *
104: * @param pluginData The plugin data
105: * @throws Exception if there was a problem deleting that data
106: */
107: public static void deletePluginData(ATPluginData pluginData) throws Exception {
108: try {
109: DomainAccessObject access =
110:
DomainAccessObjectFactory.getInstance().getDomainAccessObject(ATPluginData.class);
111: access.getLongSession();
112: access.deleteLongSession(pluginData);
113: } catch(Exception e) {
114: e.printStackTrace();
115: throw new Exception("Error Deleting Plugin Data to Database ...");
116: }
117: }
118:
119: /**
120: * Method to get all the saved data for a certain plugin
121: *
122: * @param pluginName The name of the plugin
123: * @return Collection containing any data they found
124: * @throws Exception
125: */
126: public static Collection getData(String pluginName) throws Exception {
127: try {
128: DomainAccessObject access =
129:
DomainAccessObjectFactory.getInstance().getDomainAccessObject(ATPluginData.class);
130: return access.findByPropertyValue("pluginName", pluginName);
131: } catch(Exception e) {
132: e.printStackTrace();
133: throw new Exception("Error Getting Plugin Data from Database ...");
134: }
135:
136: }
137:
138: /**
139: * Method to get all the saved data for a particular plugin and data type
140: *
141: * @param pluginName The name of the plugin
142: * @param dataType The data type of the plugin
143: * @return A collection containing any data that was found
144: * @throws Exception is thrown of there is a problem find the data
145: */
146: public static Collection getData(String pluginName, String dataType) throws
Exception {
147: try {
148: DomainAccessObject access =
149:
DomainAccessObjectFactory.getInstance().getDomainAccessObject(ATPluginData.class);
150: ATPluginData pluginData = new ATPluginData();
151: pluginData.setPluginName(pluginName);
152: pluginData.setDataType(dataType);
153: return access.findByExample(pluginData);
154: } catch(Exception e) {
155: e.printStackTrace();
156: throw new Exception("Error Getting Plugin Data from Database ...");
157: }
158: }
159:
160: /**
161: * Method to return a string object or xml encoded object
162: * found in the database. If more than one data object with
163: * the same name is found then the first one is return.
164: *
165: * @param pluginName The name of the plugin
166: * @param dataName The name of the data
167: * @return The data object
168: * @throws Exception If there is a problem finding the data from the database
169: */
170: public static Object getDataByName(String pluginName, String dataName) throws
Exception {
171: try {
172: DomainAccessObject access =
173:
DomainAccessObjectFactory.getInstance().getDomainAccessObject(ATPluginData.class);
174: ATPluginData pluginData = new ATPluginData();
175: pluginData.setPluginName(pluginName);
176: pluginData.setDataName(dataName);
177: Collection collection = access.findByExample(pluginData);
178:
179: // get the plugin data object returned from the database only return the
first one
180: if(collection != null) {
181: Object[] dataFound = collection.toArray();
182: pluginData = (ATPluginData)dataFound[0];
183: if(pluginData.getIsObject()) { // xml encoded object so convert it to
an object
184: return getObjectFromPluginData(pluginData);
185: } else { // just return the plain data string
186: return pluginData.getDataString();
187: }
188: } else {
189: return null;
190: }
191: } catch(Exception e) {
192: e.printStackTrace();
193: throw new Exception("Error Getting Plugin Data from Database ...");
194: }
195: }
196:
197: /**
198: * Method to return an object from plugin data using
199: * xstream to convert the saved xml to an object
200: *
201: * @param pluginData The ATPluginDataContaining the xml encoded object
202: * @return The converted object or null if conversion can't be done
203: */
204: public static Object getObjectFromPluginData(ATPluginData pluginData) {
205: if(pluginData != null && pluginData.getIsObject()) {
206: XStream xstream = new XStream();
207: return xstream.fromXML(pluginData.getDataString());
208: } else {
209: return null;
210: }
211: }
212:
213: /**
214: * Method to save an AT record to the database.
215: *
216: * @param record The AT record to save to the database.
217: * @throws Exception if there is a problem saving the record to the database
218: */
219: public static void saveRecordToDatabase(DomainObject record) throws Exception {
220: try {
221: Class clazz = record.getClass();
222:
223: DomainAccessObject access =
224:
DomainAccessObjectFactory.getInstance().getDomainAccessObject(clazz);
225: access.getLongSession();
226: access.updateLongSession(record);
227: } catch(Exception e) {
228: e.printStackTrace();
229: throw new Exception("Error Saving Record to Database ...");
230: }
231: }
232:
233: /**
234: * Method to valid a AT record. Calling this ensures that no invalid
235: * records are saved to the database
236: *
237: * @param component UI component that is requesting validation of the record
238: * @param record The AT record to validate
239: * @return true if the record valide, false otherwise
240: */
241: public static boolean validateRecord(Component component, DomainObject record) {
242: ATValidator validator = ValidatorFactory.getInstance().getValidator(record);
243: if (validator == null) {
244: //nothing registered so just return true
245: return true;
246: } else {
247: ValidationResult validationResult = validator.validate();
248: if (validationResult.hasErrors()) {
249: JGoodiesValidationUtils.showValidationMessage(
250: component,
251: "To save the record, please fix the following errors:",
252: validationResult);
253: return false;
254: }
255: if (validationResult.hasWarnings()) {
256: JGoodiesValidationUtils.showValidationMessage(
257: component,
258: "Note: some fields are invalid.",
259: validationResult);
260: }
261: return true;
262: }
263: }
264:}