001: /*
002: * Copyright 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.kfs.context;
017:
018: import java.lang.reflect.Field;
019: import java.lang.reflect.Modifier;
020: import java.util.HashMap;
021: import java.util.Map;
022: import java.util.TreeSet;
023:
024: import org.kuali.test.ConfigureContext;
025: import org.kuali.test.suite.AnnotationTestSuite;
026: import org.kuali.test.suite.PreCommitSuite;
027: import org.springframework.aop.framework.AopProxyUtils;
028:
029: @AnnotationTestSuite(PreCommitSuite.class)
030: @ConfigureContext
031: public class TransactionalAnnotationTest extends KualiTestBase {
032:
033: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
034: .getLogger(TransactionalAnnotationTest.class);
035:
036: Map<Class, Boolean> seenClasses = new HashMap();
037:
038: public void testTransactionAnnotations() {
039: Map<String, Class> nonAnnotatedTransactionalServices = getNonAnnotatedTransactionalServices();
040: for (String beanName : new TreeSet<String>(
041: nonAnnotatedTransactionalServices.keySet())) {
042: LOG.error(String.format(
043: "Service Bean improperly annotated: %s <%s>\n",
044: beanName, nonAnnotatedTransactionalServices.get(
045: beanName).getName()));
046: }
047: int count = nonAnnotatedTransactionalServices.size();
048: StringBuffer failureMessage = new StringBuffer(
049: "Transaction support for ").append(count).append(
050: count == 1 ? " Service" : " Services").append(
051: " improperly annotated: ");
052: for (String serviceName : nonAnnotatedTransactionalServices
053: .keySet()) {
054: failureMessage.append("\t").append(serviceName)
055: .append(": ").append(
056: nonAnnotatedTransactionalServices
057: .get(serviceName));
058: }
059: assertTrue(failureMessage.toString(),
060: nonAnnotatedTransactionalServices.isEmpty());
061:
062: }
063:
064: public Map getNonAnnotatedTransactionalServices() {
065: Map<String, Class> nonAnnotatedTransactionalServices = new HashMap();
066: String[] beanNames = SpringContext.getBeanNames();
067: for (String beanName : beanNames) {
068: Object bean = null;
069: try {
070: bean = SpringContext.getBean(beanName);
071: } catch (Exception e) {
072: LOG
073: .warn("Caught exception while trying to obtain service: "
074: + beanName);
075: }
076: if (bean != null) {
077: Class beanClass = bean.getClass();
078: if (beanClass.getName().startsWith("$Proxy")) {
079: beanClass = AopProxyUtils.getTargetClass(bean);
080: }
081: if (beanClass.getName().startsWith("org.kuali")
082: && !Modifier.isAbstract(beanClass
083: .getModifiers())
084: && !beanClass.getName().endsWith("DaoOjb")
085: && !beanClass.getName().endsWith("Factory")
086: && !isClassAnnotated(beanName, beanClass)) {
087: nonAnnotatedTransactionalServices.put(beanName,
088: beanClass);
089: }
090: }
091: }
092: return nonAnnotatedTransactionalServices;
093: }
094:
095: private boolean isClassAnnotated(String beanName, Class beanClass) {
096: if (beanClass
097: .getAnnotation(org.springframework.transaction.annotation.Transactional.class) != null) {
098: return true;
099: }
100: return !shouldHaveTransaction(beanClass);
101: }
102:
103: /*
104: * Recursively seek evidence that a Transaction is necessary by examining the fields of the given class and recursively
105: * investigating its superclass.
106: */
107: private boolean shouldHaveTransaction(Class beanClass) {
108: Boolean result = seenClasses.get(beanClass);
109: if (result != null)
110: return result;
111: if (seenClasses.containsKey(beanClass)) {
112: return false;
113: }
114: seenClasses.put(beanClass, null); // placeholder to avoid recursive problems
115: result = Boolean.FALSE;
116: for (Field field : beanClass.getDeclaredFields()) {
117: String name = field.getType().getName();
118: String fieldTypeName = field.getType().getName();
119: if (fieldTypeName.startsWith("org.apache.ojb")) {
120: if (!fieldTypeName
121: .equals("org.apache.ojb.broker.metadata.DescriptorRepository")) {
122: result = Boolean.TRUE;
123: }
124: }
125: if (name.startsWith("org.kuali")) {
126: if (name.contains("Dao")) {
127: result = Boolean.TRUE;
128: }
129: if (result == false) {
130: result = shouldHaveTransaction(field.getType());
131: }
132: }
133: }
134: if (result == false) {
135: if (beanClass.getSuperclass() != null
136: && beanClass.getSuperclass().getName().startsWith(
137: "org.kuali")) {
138: result = shouldHaveTransaction(beanClass
139: .getSuperclass());
140: }
141: }
142: seenClasses.put(beanClass, result);
143: return result;
144: }
145: }
|