001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.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/ecl2.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 */
016package org.kuali.rice.krad.util;
017
018import org.apache.ojb.broker.metadata.ClassDescriptor;
019import org.apache.ojb.broker.metadata.DescriptorRepository;
020import org.apache.ojb.broker.metadata.MetadataManager;
021import org.junit.After;
022import org.junit.Before;
023import org.junit.Test;
024import org.junit.runner.RunWith;
025import org.kuali.rice.core.api.config.property.ConfigContext;
026import org.kuali.rice.core.api.util.ContextClassLoaderBinder;
027import org.kuali.rice.core.framework.config.property.SimpleConfig;
028import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
029import org.kuali.rice.krad.data.metadata.MetadataRepository;
030import org.kuali.rice.krad.datadictionary.DataDictionary;
031import org.kuali.rice.krad.service.DataDictionaryService;
032import org.mockito.Mock;
033import org.mockito.invocation.InvocationOnMock;
034import org.mockito.runners.MockitoJUnitRunner;
035import org.mockito.stubbing.Answer;
036
037import java.lang.reflect.Field;
038import java.util.HashSet;
039import java.util.Set;
040import java.util.concurrent.Callable;
041
042import static org.junit.Assert.*;
043import static org.mockito.Mockito.*;
044
045/**
046 * Tests LegacyDetectionService
047 */
048@RunWith(MockitoJUnitRunner.class)
049public class LegacyDetectorTest {
050
051    private static class DummyDataObject {}
052    private static class DummyDataObjectOjb {}
053
054    private Set<Class<?>> nonLegacyClasses = new HashSet<Class<?>>();
055
056    @Mock private MetadataManager metadataManager;
057    @Mock private MetadataRepository metadataRepository;
058    @Mock private DataDictionaryService dataDictionaryService;
059    @Mock private DataDictionary dataDictionary;
060
061    private MetadataManager existingMetadataManager = null;
062
063    private LegacyDetector detector;
064
065    @Before
066    public void setUp() throws Exception {
067
068        DescriptorRepository descriptorRepository = new DescriptorRepository();
069        when(metadataManager.getGlobalRepository()).thenReturn(descriptorRepository);
070        this.existingMetadataManager = hackOjb(metadataManager);
071
072        when(metadataRepository.contains(any(Class.class))).thenAnswer(new Answer() {
073            @Override
074            public Object answer(InvocationOnMock invocation) {
075                Class<?> type = (Class<?>)invocation.getArguments()[0];
076                return nonLegacyClasses.contains(type);
077            }
078        });
079
080        when(dataDictionaryService.getDataDictionary()).thenReturn(dataDictionary);
081
082        nonLegacyClasses.clear();
083
084        ConfigContext.init(new SimpleConfig());
085        ConfigContext.getCurrentContextConfig().removeProperty(KRADConstants.Config.KNS_ENABLED);
086        ConfigContext.getCurrentContextConfig().removeProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK);
087
088        this.detector = new LegacyDetector(this.metadataRepository, this.dataDictionaryService);
089
090        addOjbClass(DummyDataObjectOjb.class);
091    }
092
093    @After
094    public void tearDown() throws Exception {
095        unhackOjb(this.existingMetadataManager);
096    }
097
098    @Test
099    public void testKnsDisabledByDefault() {
100        assertFalse(detector.isKnsEnabled());
101    }
102
103    @Test
104    public void testKnsEnabledByConfig() {
105        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.KNS_ENABLED, "true");
106        assertTrue(detector.isKnsEnabled());
107    }
108
109    @Test
110    public void testKnsDisabledByConfig() {
111        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.KNS_ENABLED, "false");
112        assertFalse(detector.isKnsEnabled());
113    }
114
115    @Test
116    public void testLegacyDataFrameworkDisabledByDefault() {
117        assertFalse(detector.isLegacyDataFrameworkEnabled());
118    }
119
120    @Test
121    public void testLegacyDataFrameworkEnabledByKnsByDefault() {
122        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.KNS_ENABLED, "true");
123        assertTrue(detector.isLegacyDataFrameworkEnabled());
124    }
125
126    @Test
127    public void testLegacyDataFrameworkDisabledByKnsByDefault() {
128        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.KNS_ENABLED, "false");
129        assertFalse(detector.isLegacyDataFrameworkEnabled());
130    }
131
132    @Test
133    public void testLegacyDataFrameworkEnabledByConfig() {
134        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.KNS_ENABLED, "false");
135        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "true");
136        assertTrue(detector.isLegacyDataFrameworkEnabled());
137    }
138
139    @Test
140    public void testLegacyDataFrameworkDisabledByConfig() {
141        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.KNS_ENABLED, "true");
142        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "false");
143        assertFalse(detector.isLegacyDataFrameworkEnabled());
144    }
145
146    @Test
147    public void testDataObjectNotLegacyWhenDisabled() {
148        addOjbClass(DummyDataObject.class);
149        assertFalse(detector.isLegacyDataFrameworkEnabled());
150        assertFalse(detector.useLegacyForObject(new DummyDataObject()));
151        assertFalse(detector.useLegacy(DummyDataObject.class));
152    }
153
154    @Test
155    public void testDataObjectNotLegacyWhenNotLoaded() {
156        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "true");
157        assertTrue(detector.isLegacyDataFrameworkEnabled());
158        assertFalse(detector.useLegacyForObject(new DummyDataObject()));
159        assertFalse(detector.useLegacy(DummyDataObject.class));
160    }
161
162    @Test
163    public void testDataObjectIsLegacyWhenEnabledAndLoaded() {
164        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "true");
165        addOjbClass(DummyDataObject.class);
166        assertTrue(detector.isLegacyDataFrameworkEnabled());
167        assertTrue(detector.useLegacyForObject(new DummyDataObject()));
168        assertTrue(detector.useLegacy(DummyDataObject.class));
169    }
170
171    @Test
172    public void testUseLegacy_InContext() {
173        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "true");
174        detector.beginLegacyContext();
175        try {
176            // since recent changes, if it's not in OJB, it's not legacy
177            assertFalse(detector.useLegacy(DummyDataObject.class));
178            assertTrue(detector.useLegacy(DummyDataObjectOjb.class));
179        } finally {
180            detector.endLegacyContext();
181        }
182    }
183
184    @Test(expected = IllegalStateException.class)
185    public void testUseLegacy_LegacyFrameworkDisabled() {
186        detector.beginLegacyContext();
187        try {
188            detector.useLegacy(DummyDataObject.class);
189        } finally {
190            detector.endLegacyContext();
191        }
192    }
193
194    /**
195     * Verifies that if you pass an instance of java.lang.Class to useLegacyForObject, that it
196     * throws an IllegalArgumentException since you should use the useLegacy method for that
197     */
198    @Test(expected = IllegalArgumentException.class)
199    public void testUseLegacyForObject_PassingAClassIsBad() {
200        detector.useLegacyForObject(DummyDataObject.class);
201    }
202
203    @Test(expected=IllegalStateException.class)
204    public void testBeginLegacyContext_LegacyFrameworkDisabled() {
205        detector.beginLegacyContext();
206    }
207
208    @Test(expected=IllegalStateException.class)
209    public void testEndLegacyContext_NoContext() {
210        detector.endLegacyContext();
211    }
212
213    @Test
214    public void testBeginEndLegacyContext() {
215        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "true");
216        assertFalse(detector.isInLegacyContext());
217        detector.beginLegacyContext();
218        try {
219            assertTrue(detector.isInLegacyContext());
220        } finally {
221            detector.endLegacyContext();
222        }
223        assertFalse(detector.isInLegacyContext());
224    }
225
226    @Test
227    public void testNestedLegacyContext() {
228        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "true");
229        assertFalse(detector.isInLegacyContext());
230        detector.beginLegacyContext();
231        try {
232            assertTrue(detector.isInLegacyContext());
233            detector.beginLegacyContext();
234            try {
235                detector.beginLegacyContext();
236                try {
237                    assertTrue(detector.isInLegacyContext());
238                } finally {
239                    detector.endLegacyContext();
240                }
241                assertTrue(detector.isInLegacyContext());
242            } finally {
243                detector.endLegacyContext();
244            }
245            assertTrue(detector.isInLegacyContext());
246        } finally {
247            detector.endLegacyContext();
248        }
249        assertFalse(detector.isInLegacyContext());
250    }
251
252    @Test
253    public void testIllegallyNestedLegacyContext() {
254        ConfigContext.getCurrentContextConfig().putProperty(KRADConstants.Config.ENABLE_LEGACY_DATA_FRAMEWORK, "true");
255        assertFalse(detector.isInLegacyContext());
256        detector.beginLegacyContext();
257        assertTrue(detector.isInLegacyContext());
258        detector.endLegacyContext();
259        assertFalse(detector.isInLegacyContext());
260        try {
261            detector.endLegacyContext();
262            fail("Should have thrown IllegalStateException");
263        } catch (IllegalStateException e) {
264            // this should happen!
265        }
266    }
267
268    @Test
269    public void testIsOjbLoadedClass() {
270        assertFalse(detector.isOjbLoadedClass(DummyDataObject.class));
271        assertTrue(detector.isOjbLoadedClass(DummyDataObjectOjb.class));
272    }
273
274    /**
275     * Verifies that isLegacyManaged reports that the object is legacy managed if it has a BusinessObjectEntry and
276     * nothing else.
277     */
278    @Test
279    public void testIsLegacyManaged_withBusinessObjectEntry() {
280        assertFalse(detector.isLegacyManaged(DummyDataObject.class));
281        when(dataDictionary.getBusinessObjectEntry(DummyDataObject.class.getName())).thenReturn(
282                new BusinessObjectEntry());
283        assertTrue(detector.isLegacyManaged(DummyDataObject.class));
284        assertFalse(detector.isLegacyManaged(String.class));
285        verify(dataDictionary, times(2)).getBusinessObjectEntry(DummyDataObject.class.getName());
286        verify(dataDictionary, times(3)).getBusinessObjectEntry(anyString());
287    }
288
289    /**
290     * Verifies that isLegacyManaged reports that the object is legacy managed if it is mapped in OJB.
291     */
292    @Test
293    public void testIsLegacyManaged_ojbLoaded() {
294        assertFalse(detector.isLegacyManaged(DummyDataObject.class));
295        // it must be mapped in ojb
296        assertTrue(detector.isLegacyManaged(DummyDataObjectOjb.class));
297    }
298
299    /**
300     * Ensures false is returned form isOjbLoadedClass if OJB is not on the classpath.
301     */
302    @Test
303    public void testIsOjbLoadedClass_NoOjbOnClasspath() throws Exception {
304        // set an empty classloader to cause the check to load OJB MetadataManager to fail from inside isOjbLoadedClass
305        ContextClassLoaderBinder.doInContextClassLoader(ClassLoader.getSystemClassLoader().getParent(),
306                new Callable<Object>() {
307                    @Override
308                    public Object call() throws Exception {
309                        assertFalse(detector.isOjbLoadedClass(DummyDataObjectOjb.class));
310                        return null;
311                    }
312                });
313    }
314
315    private void addOjbClass(Class<?> type) {
316        ClassDescriptor classDescriptor = new ClassDescriptor(metadataManager.getGlobalRepository());
317        classDescriptor.setClassOfObject(type);
318        metadataManager.getGlobalRepository().put(type, classDescriptor);
319    }
320
321    /**
322     * Hacks OJB to put a stubbed MetadataManager into place.
323     */
324    private MetadataManager hackOjb(MetadataManager mockMetadataManager) throws Exception {
325        Field singletonField = MetadataManager.class.getDeclaredField("singleton");
326        singletonField.setAccessible(true);
327        MetadataManager existingMetadataManager = (MetadataManager)singletonField.get(null);
328        singletonField.set(null, mockMetadataManager);
329        return existingMetadataManager;
330    }
331
332    private void unhackOjb(MetadataManager existingMetadataManager) throws Exception{
333        Field singletonField = MetadataManager.class.getDeclaredField("singleton");
334        singletonField.setAccessible(true);
335        singletonField.set(null, this.existingMetadataManager);
336
337    }
338
339}