Supporting Open Source Software for Education
The Problem ScenarioSuppose 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 ValidationBy 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.