Fork me on GitHub

n. Slang a rough lawless young Kuali developer.
[perhaps variant of Houlihan, Irish surname]
kualiganism n

Blog of an rSmart Java Developer. Full of code examples, solutions, best practices, et al.

Tuesday, April 27, 2010

KFS Inheritance/Composition with Private Methods

The Problem Scenario

Suppose an instance arrives where you need to change the functionality of KFS slightly, but the method you want to override is declared private. Need an example? I have one. Read on.

Digester and Namespace Validation

By default, Digester within KFS has namespace validation off. Suppose you want to turn it on. Now you run into exactly such issue.
package edu.arizona.kfs.sys.batch;
...
...
public class XmlBatchInputFileTypeBase extends org.kuali.kfs.sys.batch.XmlBatchInputFileTypeBase {
/**
* @see org.kuali.kfs.sys.batch.BatchInputFileType#parse(byte[])
*/
public Object parse(byte[] fileByteContent) throws ParseException {
if (fileByteContent == null) {
LOG.error("an invalid(null) argument was given");
throw new IllegalArgumentException("an invalid(null) argument was given");
}

// handle zero byte contents, xml parsers don't deal with them well
if (fileByteContent.length == 0) {
LOG.error("an invalid argument was given, empty input stream");
throw new IllegalArgumentException("an invalid argument was given, empty input stream");
}

// validate contents against schema
ByteArrayInputStream validateFileContents = new ByteArrayInputStream(fileByteContent);
validateContentsAgainstSchema(getSchemaLocation(), validateFileContents);

// setup digester for parsing the xml file

Digester digester = null;
try {
Method digesterMethod = getClass().getDeclaredMethod("buildDigester", String.class, String.class);
m.setAccessible(true); // Private? Who cares?! I sure don't.
m.invoke(this, getSchemaLocation(), getDigestorRulesFileName());
}
catch(IllegalAccessExption e) {
// Not since it's accessible now.
}
catch(InvocationTargetException e) {
// Something else naughty happened
}


Object parsedContents = null;
try {
ByteArrayInputStream parseFileContents = new ByteArrayInputStream(fileByteContent);
parsedContents = digester.parse(validateFileContents);
}
catch (Exception e) {
LOG.error("Error parsing xml contents", e);
throw new ParseException("Error parsing xml contents: " + e.getMessage(), e);
}

return parsedContents;
}

}

The trick is in

Digester digester = null;
try {
Method digesterMethod = getClass().getDeclaredMethod("buildDigester", String.class, String.class);
m.setAccessible(true); // Private? Who cares?! I sure don't.
m.invoke(this, getSchemaLocation(), getDigestorRulesFileName());
}
catch(IllegalAccessExption e) {
// Not since it's accessible now.
}

By calling m.setAccessible(true), we can still call the private method. many call this a hack because it ignores the private access. I think many people are confused in what that means. The control is rather just for managing scope and maintaining OO encapsulation. These directives are not intended for security. They are intended to enforce OO. If we wanted to restrict method access, a SecurityManager implementation would be more appropriate. IMHO, this is an entirely acceptable thing to do in this situation. That being that we are being restricted by a flaw in the API from doing something we should be allowed to do. This happens often, and is a much better alternative than duplicating code.

With this, we can get away with extending code and overriding behavior.

No comments:

Post a Comment