001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.bo;
017:
018: import java.io.File;
019: import java.io.FileFilter;
020: import java.io.FilenameFilter;
021: import java.lang.reflect.InvocationTargetException;
022: import java.lang.reflect.Method;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Set;
027:
028: import org.apache.commons.beanutils.PropertyUtils;
029: import org.apache.commons.lang.StringUtils;
030: import org.kuali.core.bo.PersistableBusinessObject;
031: import org.kuali.core.bo.PersistableBusinessObjectBase;
032: import org.kuali.core.exceptions.IntrospectionException;
033: import org.kuali.core.service.PersistenceService;
034: import org.kuali.core.service.PersistenceStructureService;
035: import org.kuali.core.service.impl.PersistenceTestServiceImpl;
036: import org.kuali.kfs.context.KualiTestBase;
037: import org.kuali.kfs.context.SpringContext;
038: import org.kuali.test.ConfigureContext;
039:
040: /**
041: * This class verifies some rules about setting up Business Objects. Primary Key & References: Object - has attribute, a getter, and
042: * setter OJB - not setup as anonymous When no column of the FK is also part of the current objects PK, then anonymous attributes
043: * should be used. So: Object - no attribute, no getter, no setter OJB - FK anonymous Should have a a public constructor that takes
044: * no arguments Should not initialize other Business Objects, but may initialize other types such as Lists No BO initialization
045: * should take place in getters (e.g. "lazy initialization")
046: */
047: @ConfigureContext
048: public class BusinessObjectStructureTest extends KualiTestBase {
049:
050: final static File SOURCEDIR = new File(
051: "/java/projects/kuali/work/src");
052: final static FileDirectoryFilter DIRECTORY_FILTER = new FileDirectoryFilter();
053: final static FilenameFilter JAVA_FILE_FILTER = new FileExtensionFilter(
054: ".java");
055: private final static String SEPARATOR = File.separator;
056: private final static String PACKAGE_PREFIX = "org.kuali";
057:
058: PersistenceTestServiceImpl persistenceTestService = new PersistenceTestServiceImpl();
059:
060: /**
061: * Filter to match files having a particular extension
062: */
063: static class FileExtensionFilter implements FilenameFilter {
064: private String extension;
065: private List ignoreNames;
066:
067: public FileExtensionFilter(String extension) {
068: this .extension = extension;
069: }
070:
071: public FileExtensionFilter(String extension, List ignoreNames) {
072: this .extension = extension;
073: this .ignoreNames = ignoreNames;
074: }
075:
076: public boolean accept(File arg0, String arg1) {
077: boolean passesNameCheck = true;
078: if (ignoreNames != null) {
079: for (Iterator iter = ignoreNames.iterator(); iter
080: .hasNext();) {
081: String name = (String) iter.next();
082: if (StringUtils.contains(arg1, name)) {
083: passesNameCheck = false;
084: }
085: }
086: }
087: return passesNameCheck && arg1.endsWith(extension);
088: }
089:
090: }
091:
092: /**
093: * Filter to match Directories
094: */
095: static class FileDirectoryFilter implements FileFilter {
096:
097: public boolean accept(File file) {
098: return file.isDirectory();
099: }
100: }
101:
102: /**
103: * This method recursively iterates through a given directory and builds a set of files as allowed by the given filter
104: *
105: * @param directory - directory from which to start
106: * @param filter - FilenameFilter that determines whether a file is allowed in the result
107: * @return Set of Files matching filter
108: */
109: public Set recursivelyFindFilteredFiles(File directory,
110: FilenameFilter filter) {
111: List list = null;
112: try {
113: list = java.util.Arrays.asList(directory.listFiles(filter));
114: } catch (NullPointerException e) {
115: System.err.println("cannot find java source at: "
116: + SOURCEDIR);
117: return new HashSet();
118: }
119: HashSet result = new HashSet(list);
120:
121: File[] subFolder = directory.listFiles(DIRECTORY_FILTER);
122: for (int i = 0; i < subFolder.length; i++) {
123: result.addAll(recursivelyFindFilteredFiles(subFolder[i],
124: filter));
125: }
126:
127: return result;
128:
129: }
130:
131: /**
132: * This method converts a File like /java/project/kuali/work/src/org/kuali/MyClass.java into a class e.g. org.kuali.MyClass
133: *
134: * @param file
135: * @return Class
136: * @throws ClassNotFoundException
137: */
138: public Class fileToClass(File file) throws ClassNotFoundException {
139: String filename = file.toString();
140: StringBuffer result = new StringBuffer(filename
141: .substring(filename
142: .indexOf("org" + SEPARATOR + "kuali")));
143:
144: int index = result.indexOf(SEPARATOR);
145: while (index >= 0) {
146: result.replace(index, index + 1, ".");
147: index = result.indexOf(SEPARATOR);
148: }
149:
150: result.delete(result.lastIndexOf(".java"), result.length());
151:
152: return Class.forName(result.toString());
153: }
154:
155: /**
156: * This method returns a set of Files from SOURCEDIR with an extension of .java
157: *
158: * @return Set of Files
159: */
160: public Set findJavaSourceFiles() {
161: return recursivelyFindFilteredFiles(SOURCEDIR, JAVA_FILE_FILTER);
162: }
163:
164: public void verify(Object object, String property,
165: boolean expectGetter, boolean expectSetter) {
166: Class klass = object.getClass();
167:
168: boolean hasGetter = hasGetter(object, property);
169: boolean hasSetter = hasSetter(object, property);
170:
171: System.out.println(property + " "
172: + (hasGetter ? " Getter" : "")
173: + (hasSetter ? " Setter" : ""));
174:
175: if (hasGetter != expectGetter) {
176: if (expectGetter) {
177: System.err.println("Getter expected but not present");
178: } else {
179: System.err.println("Getter not expected but present");
180: }
181: }
182:
183: if (hasSetter != expectSetter) {
184: if (expectSetter) {
185: System.err.println("Setter expected but not present");
186: } else {
187: System.err.println("Setter not expected but present");
188: }
189: }
190:
191: }
192:
193: public void verifyNestedObjectsAreNull(Object object)
194: throws IllegalArgumentException, IllegalAccessException,
195: InvocationTargetException {
196: Method[] methods = object.getClass().getDeclaredMethods();
197: for (int i = 0; i < methods.length; i++) {
198: Method method = methods[i];
199: Package returnPackage = method.getReturnType().getPackage();
200: if (returnPackage != null
201: && returnPackage.getName().startsWith(
202: PACKAGE_PREFIX)) {
203: if (method.getParameterTypes().length == 0) {
204: Object result = method.invoke(object,
205: new Object[] {});
206: if (result != null) {
207: System.err
208: .println("expected null when running "
209: + method.getName()
210: + " but got: " + result);
211: }
212: } else {
213: System.err
214: .println("Unable to check method because it requires parameters: "
215: + method.getName());
216: }
217: }
218: }
219: }
220:
221: public void verifyPrimaryKey(Object object) {
222:
223: try {
224: Iterator primaryKeys = SpringContext.getBean(
225: PersistenceService.class).getPrimaryKeyFieldValues(
226: object).keySet().iterator();
227: System.out.println("Primary keys:");
228: while (primaryKeys.hasNext()) {
229: String primaryKey = (String) primaryKeys.next();
230: verify(object, primaryKey, true, true);
231: }
232: } catch (IntrospectionException e) {
233: System.err.println("Unable to verify primary key due to: "
234: + e);
235: }
236:
237: }
238:
239: public boolean hasGetter(Object object, String property) {
240: return PropertyUtils.isReadable(object, property);
241: }
242:
243: public boolean hasSetter(Object object, String property) {
244: return PropertyUtils.isWriteable(object, property);
245: }
246:
247: public boolean isBusinessObjectClass(Class klass) {
248:
249: if (klass == null)
250: return false; // not sure why this would ever be null, but this seems to work
251:
252: if (klass.equals(Object.class)) {
253: return false;
254: }
255:
256: Class[] interfaces = klass.getInterfaces();
257: for (int i = 0; i < interfaces.length; i++) {
258: if (PersistableBusinessObject.class.equals(interfaces[i])) {
259: return true;
260: }
261: }
262: if (klass.equals(PersistableBusinessObjectBase.class)) {
263: return true;
264: }
265: return isBusinessObjectClass(klass.getSuperclass());
266: }
267:
268: public void test() throws Exception {
269:
270: Iterator candidates = findJavaSourceFiles().iterator();
271: while (candidates.hasNext()) {
272: File nextFile = (File) candidates.next();
273: Class klass = fileToClass(nextFile);
274: if (isBusinessObjectClass(klass)) {
275: System.out.println(klass.getName());
276: Object object = null;
277: try {
278: object = klass.newInstance();
279: } catch (InstantiationException e) {
280: System.err.println("Cannot instantiate: "
281: + klass.getName());
282: continue;
283: } catch (IllegalAccessException e) {
284: System.err.println("Illegal access: "
285: + e.getMessage());
286: continue;
287: }
288:
289: if (!SpringContext.getBean(
290: PersistenceStructureService.class)
291: .isPersistable(klass)) {
292: System.err.println("class is not persistable: "
293: + klass);
294: return;
295: }
296:
297: verifyPrimaryKey(object);
298:
299: verifyNestedObjectsAreNull(object);
300:
301: System.out.println("Standard properties:");
302: verify(object, "versionNumber", true, true);
303: verify(object, "objectId", true, true);
304: }
305: }
306:
307: }
308:
309: }
|