View Javadoc
1   /*
2    * Copyright 2006-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.kuali.rice.krad.data.provider.util;
18  
19  import com.google.common.collect.Sets;
20  import org.junit.Test;
21  import org.kuali.rice.krad.data.DataObjectService;
22  import org.kuali.rice.krad.data.KradDataServiceLocator;
23  import org.kuali.rice.krad.data.util.ReferenceLinker;
24  import org.kuali.rice.krad.test.KRADTestCase;
25  import org.kuali.rice.krad.test.conference.ConferenceSession;
26  import org.kuali.rice.krad.test.conference.PresenterInfo;
27  import org.kuali.rice.krad.test.conference.Room;
28  import org.kuali.rice.krad.test.conference.SessionCoordinator;
29  import org.kuali.rice.krad.test.conference.SessionPresenter;
30  
31  import java.util.ArrayList;
32  import java.util.HashSet;
33  import java.util.List;
34  import java.util.Set;
35  
36  import static org.junit.Assert.*;
37  import static org.kuali.rice.krad.data.PersistenceOption.FLUSH;
38  import static org.kuali.rice.krad.data.PersistenceOption.LINK_KEYS;
39  
40  public class ReferenceLinkerIntegrationTest extends KRADTestCase {
41  
42      private DataObjectService dataObjectService;
43      private ReferenceLinker referenceLinker;
44  
45      @Override
46      protected void setUpInternal() throws Exception {
47          super.setUpInternal();
48          dataObjectService = KradDataServiceLocator.getDataObjectService();
49          referenceLinker = new ReferenceLinker();
50          referenceLinker.setDataObjectService(dataObjectService);
51      }
52  
53      @Test
54      public void testIdentityLinking() {
55  
56          // first, create a SessionCoordinator and save it
57  
58          SessionCoordinator savedCoordinator = new SessionCoordinator();
59          savedCoordinator.setName("Me");
60          savedCoordinator = getDataObjectService().save(savedCoordinator);
61          assertNotNull(savedCoordinator.getId());
62  
63          // now let's create an ConferenceSession and see if we can link to the SessionCoordinator using it's id
64  
65          ConferenceSession session = new ConferenceSession();
66          SessionCoordinator coordinator = new SessionCoordinator();
67          session.setAltCoordinator1(coordinator);
68          coordinator.setId(savedCoordinator.getId());
69  
70          // try to perform the linking
71          Set<String> changes = new HashSet<String>();
72          changes.add("altCoordinator1.id");
73          referenceLinker.linkChanges(session, changes);
74  
75          // assert that it loaded the relationship fresh and copied the FK back to the session
76          SessionCoordinator fetchedCoordinator = session.getAltCoordinator1();
77          assertNotNull(fetchedCoordinator);
78          assertEquals(savedCoordinator.getId(), fetchedCoordinator.getId());
79          assertEquals("Me", fetchedCoordinator.getName());
80          assertEquals(savedCoordinator.getId(), session.getAltCoordinator1Id());
81  
82          // it should have also added our session to the list of alt sessions on the SessionCordinator to complete
83          // the bi-directional relationship
84          assertEquals(1, fetchedCoordinator.getAltCoordinatedSessions1().size());
85          assertEquals(session, fetchedCoordinator.getAltCoordinatedSessions1().get(0));
86  
87      }
88  
89      @Test
90      public void testIdentityLinking_InvalidKey() {
91          ConferenceSession session = new ConferenceSession();
92          // set to an account manager id that doesn't exist
93          session.setAltCoordinator1Id(Long.valueOf(Long.MAX_VALUE));
94  
95          // now attempt to link, it shouldn't fail, but it also shouldn't do anything
96          Set<String> changes = new HashSet<String>();
97          changes.add("altCoordinator1Id");
98          referenceLinker.linkChanges(session, changes);
99          assertEquals(Long.valueOf(Long.MAX_VALUE), session.getAltCoordinator1Id());
100         assertNull(session.getAltCoordinator1());
101 
102         // now set a dummy coordinator on the session, when we link, it should be replaced with null
103         SessionCoordinator sc = new SessionCoordinator();
104         sc.setName("admin");
105         sc.setId(Long.valueOf(12345L));
106         session.setAltCoordinator1(sc);
107         referenceLinker.linkChanges(session, changes);
108         assertEquals(Long.valueOf(Long.MAX_VALUE), session.getAltCoordinator1Id());
109         assertNull(session.getAltCoordinator1());
110 
111     }
112 
113     @Test
114     public void testAttributeLinking() {
115 
116         // first, create a SessionCoordinator and save it
117 
118         SessionCoordinator sc = new SessionCoordinator();
119         sc.setName("admin");
120         sc = getDataObjectService().save(sc);
121         assertNotNull(sc.getId());
122 
123         // now let's create a ConferenceSession and see if we can link to the SessionCoordinator using it's FK
124 
125         ConferenceSession session = new ConferenceSession();
126         session.setAltCoordinator1Id(sc.getId());
127 
128         // try to perform the linking
129         Set<String> changes = new HashSet<String>();
130         changes.add("altCoordinator1Id");
131         referenceLinker.linkChanges(session, changes);
132 
133         // assert that it loaded the relationship fresh using the FK on the session
134 
135         // first, verify the FK on the session was not modified
136         assertEquals(session.getAltCoordinator1Id(), sc.getId());
137         // next, make sure that SessionCoordinator is no longer null
138         SessionCoordinator fetchedSc = session.getAltCoordinator1();
139         assertNotNull(fetchedSc);
140 
141         // the fetched SessionCoordinator should have all of it's values set appropriately
142         assertEquals(session.getAltCoordinator1Id(), fetchedSc.getId());
143         assertEquals("admin", fetchedSc.getName());
144 
145     }
146 
147     @Test
148     public void testAttributeLinking_InvalidKey() {
149 
150         // now let's create an ConferenceSession with an SessionCoordinator with an invalid id
151 
152         ConferenceSession session = new ConferenceSession();
153         SessionCoordinator sc = new SessionCoordinator();
154         sc.setId(Long.valueOf(-1));
155         session.setAltCoordinator1(sc);
156         session.setAltCoordinator1Id(Long.valueOf(-2));
157 
158         // try to perform the linking
159         Set<String> changes = new HashSet<String>();
160         changes.add("altCoordinator1.id");
161         referenceLinker.linkChanges(session, changes);
162 
163         // the result of the linking should be that the coordinator is unchanged since we don't nullify values
164         assertEquals(Long.valueOf(-1), session.getAltCoordinator1().getId());
165 
166         // it should still copy the FK value back though,
167         // so the altCoordinator1Id on the session itself should get changed from -2 to -1
168         assertEquals(Long.valueOf(-1), session.getAltCoordinator1Id());
169 
170     }
171 
172     /**
173      * Tests that an FK gets copied back when linking after a change.
174      */
175     @Test
176     public void testAttributeLinking_Backward() {
177         // create and save the Room first
178         Room room = new Room();
179         room.setNumber("1");
180         room.setBuildingName("Building");
181         room = getDataObjectService().save(room);
182 
183         ConferenceSession session = new ConferenceSession();
184         session.setRoom(room);
185         session.setAltRoom1(room);
186         session.setAltRoom2(room);
187 
188         // now let's try linking changes
189         referenceLinker.linkChanges(session, Sets.newHashSet("room", "altRoom1", "altRoom2"));
190 
191         // after linking, both the id's for alt room1 and room2 should have been copied back for us
192         assertEquals(room.getId(), session.getAltRoom1Id());
193         assertEquals(room.getId(), session.getAltRoom2Id());
194     }
195 
196     @Test
197     public void testIdentityAndAttributeLinking() {
198 
199         // first, create an SessionCoordinator and save it
200 
201         SessionCoordinator sc = new SessionCoordinator();
202         sc.setName("admin");
203         sc = getDataObjectService().save(sc);
204         assertNotNull(sc.getId());
205 
206         // now let's create an ConferenceSession with a reference to an alt coordinator with a different id, but the FK to altCoordinator1Id being set properly
207 
208         ConferenceSession session = new ConferenceSession();
209         SessionCoordinator coordinator = new SessionCoordinator();
210         session.setAltCoordinator1(coordinator);
211         coordinator.setId(Long.valueOf(-1));
212         session.setAltCoordinator1Id(sc.getId());
213 
214         // now we have a situation where session.altCoordinator1Id and session.altCoordinator1.id are different but both were modified!
215 
216         // in these cases, as per the spec for this, the value in the foreign key should take precedence, so let's try some linking
217         Set<String> changes = new HashSet<String>();
218         changes.add("altCoordinator1.id");
219         changes.add("altCoordinator1Id");
220         referenceLinker.linkChanges(session, changes);
221 
222         assertNotNull(session.getAltCoordinator1());
223         assertEquals(sc.getId(), session.getAltCoordinator1Id());
224         assertEquals(sc.getId(), session.getAltCoordinator1().getId());
225         assertEquals("admin", session.getAltCoordinator1().getName());
226 
227         // now if we change this slightly so that altCoordinator1Id on the session is invalid, it should replace the altCoordinator1
228         // reference with a null value
229         session.setAltCoordinator1Id(Long.valueOf(-1));
230         referenceLinker.linkChanges(session, changes);
231         assertEquals(Long.valueOf(-1), session.getAltCoordinator1Id());
232         // account manager should be null
233         assertNull(session.getAltCoordinator1());
234     }
235 
236     @Test
237     public void testParentObjectWithNonUpdatableFK() {
238         ConferenceSession session = new ConferenceSession();
239         session.setSessionTitle("blah");
240         SessionCoordinator coordinator = new SessionCoordinator();
241         coordinator.setName("someuser");
242         session.setAltCoordinator2(coordinator);
243 
244         // first let's do a save with no linking
245         ConferenceSession savedSession = dataObjectService.save(session, FLUSH);
246         assertNotNull(savedSession.getId());
247         assertNotNull(savedSession.getAltCoordinator2().getId());
248         // the altCoordinator2Id *should* be null, JPA does not automatically link this up for us
249         assertNull(savedSession.getAltCoordinator2Id());
250 
251         // now if we are feeling fiesty here we can link at this point and end up getting our altCoordinator2Id updated
252         referenceLinker.linkChanges(savedSession, Sets.newHashSet("altCoordinator2.id"));
253         assertEquals(savedSession.getAltCoordinator2().getId(), savedSession.getAltCoordinator2Id());
254 
255         // but what we really want is for it to link that up for us automagically after we save the session, is that
256         // so much to ask? Well, we *can* ask for that with the LINK_KEYS parameter
257         ConferenceSession savedSession2 = dataObjectService.save(session, LINK_KEYS);
258         // first, let's just double check to make sure this is a different session than the last one we saved (note
259         // that we saved the "session", not the "savedSession"
260         assertNotEquals(savedSession.getId(), savedSession2.getId());
261         assertNotEquals(savedSession.getAltCoordinator2().getId(), savedSession2.getAltCoordinator2().getId());
262 
263         // at this point our new session should have it's altCoordinator2Id set properly
264         assertEquals(savedSession2.getAltCoordinator2().getId(), savedSession2.getAltCoordinator2Id());
265 
266         // now if we set the altCoordinator2 to null and save, we ought to get reset back to null on the FK
267         savedSession2.setAltCoordinator2(null);
268         assertNotNull(savedSession2.getAltCoordinator2Id());
269 
270         // save it, altCoordinator2 should still be null, but the altCoordinator2Id on the session should be null now
271         savedSession2 = dataObjectService.save(savedSession2, LINK_KEYS);
272         assertNull(savedSession2.getAltCoordinator2());
273         assertNull(savedSession2.getAltCoordinator2Id());
274 
275         // now if we set the altCoordinator2 to null and link, we ought to get reset back to null on the FK
276         savedSession.setAltCoordinator2(null);
277         assertNotNull(savedSession.getAltCoordinator2Id());
278 
279         // link it
280         referenceLinker.linkChanges(savedSession, Sets.newHashSet("altCoordinator2"));
281 
282         // altCoordinator2 should still be null, but the altCordinator2Id on the session should be null now as well
283         assertNull(savedSession.getAltCoordinator2());
284         assertNull(savedSession.getAltCoordinator2Id());
285 
286     }
287 
288     /**
289      * Tests that linking recurses down through collections properly.
290      */
291     @Test
292     public void testLinkingThroughCollection() {
293         // create 3 PresenterInfo
294         PresenterInfo presenter1 = new PresenterInfo();
295         presenter1.setName("presenter1");
296         presenter1.setInstitution("institution1");
297         PresenterInfo presenter2 = new PresenterInfo();
298         presenter2.setName("presenter2");
299         presenter2.setInstitution("institution2");
300         PresenterInfo presenter3 = new PresenterInfo();
301         presenter3.setName("presenter3");
302         presenter3.setInstitution("institution1");
303         presenter1 = getDataObjectService().save(presenter1, FLUSH);
304         presenter2 = getDataObjectService().save(presenter2, FLUSH);
305         presenter3 = getDataObjectService().save(presenter3, FLUSH);
306 
307         // now create the conference session and add 3 session presenters referencing those three presenter info
308         ConferenceSession session = new ConferenceSession();
309 
310         // set presenter 1 using it's id
311         SessionPresenter sessionPresenter1 = new SessionPresenter();
312         sessionPresenter1.setPrimary(true);
313         sessionPresenter1.setPresenterId(presenter1.getId());
314 
315         // set presenter 2 using the id on the reference object
316         SessionPresenter sessionPresenter2 = new SessionPresenter();
317         PresenterInfo presenter2Id = new PresenterInfo();
318         presenter2Id.setId(presenter2.getId());
319         sessionPresenter2.setPresenter(presenter2Id);
320 
321         // set presenter 3 directly
322         SessionPresenter sessionPresenter3 = new SessionPresenter();
323         sessionPresenter3.setPresenter(presenter3);
324 
325         // add all three to the session
326         session.getPresenters().add(sessionPresenter1);
327         session.getPresenters().add(sessionPresenter2);
328         session.getPresenters().add(sessionPresenter3);
329 
330         // now link us some changes
331         Set<String> changes = Sets.newHashSet(
332                 "presenters[0].presenterId",
333                 "presenters[1].presenter.id",
334                 "presenters[2].presenter");
335         referenceLinker.linkChanges(session, changes);
336 
337         // Assert that everything linked properly
338 
339         // check the first SessionPresenter
340         SessionPresenter linkedPresenter1 = session.getPresenters().get(0);
341         assertEquals(session, linkedPresenter1.getSession());
342         assertNotNull(linkedPresenter1.getPresenter());
343         assertEquals(presenter1.getId(), linkedPresenter1.getPresenterId());
344         assertEquals(presenter1.getId(), linkedPresenter1.getPresenter().getId());
345         assertEquals(presenter1.getName(), linkedPresenter1.getPresenter().getName());
346 
347         // check the second SessionPresenter
348         SessionPresenter linkedPresenter2 = session.getPresenters().get(1);
349         assertEquals(session, linkedPresenter2.getSession());
350         assertNotNull(linkedPresenter2.getPresenter());
351         assertEquals(presenter2.getId(), linkedPresenter2.getPresenterId());
352         assertEquals(presenter2.getId(), linkedPresenter2.getPresenter().getId());
353         assertEquals(presenter2.getName(), linkedPresenter2.getPresenter().getName());
354 
355         // check the third SessionPresenter
356         SessionPresenter linkedPresenter3 = session.getPresenters().get(2);
357         assertEquals(session, linkedPresenter3.getSession());
358         assertNotNull(linkedPresenter3.getPresenter());
359         assertEquals(presenter3.getId(), linkedPresenter3.getPresenterId());
360         assertEquals(presenter3.getId(), linkedPresenter3.getPresenter().getId());
361         assertEquals(presenter3.getName(), linkedPresenter3.getPresenter().getName());
362 
363         // save and sync keys
364         ConferenceSession savedSession = getDataObjectService().save(session, LINK_KEYS);
365         assertNotNull(savedSession.getId());
366         // check that the presenters got their ids and got updated with the session id
367         for (SessionPresenter sessionPresenter : savedSession.getPresenters()) {
368             String presenterName = sessionPresenter.getPresenter().getName();
369             assertNotNull("Session presenter should have generated an id for presenter " + presenterName, sessionPresenter.getId());
370             assertEquals("Session id was not linked for presenter " + presenterName, savedSession.getId(), sessionPresenter.getSessionId());
371         }
372 
373         // now let's try adding another SessionPresenter, when we link this one, it should get linked in a similar
374         // fashion as the others, but also should get it's session id set properly since our parent session has an id
375         SessionPresenter sessionPresenter4 = new SessionPresenter();
376         sessionPresenter4.setPresenterId(presenter1.getId());
377         savedSession.getPresenters().add(sessionPresenter4);
378 
379         referenceLinker.linkChanges(savedSession, Sets.newHashSet("presenters[3].presenterId"));
380 
381         // verify everything was linked properly
382         assertEquals(presenter1.getId(), sessionPresenter4.getPresenterId());
383         assertNotNull(sessionPresenter4.getPresenter());
384         assertEquals(presenter1, sessionPresenter4.getPresenter());
385         assertEquals(savedSession, sessionPresenter4.getSession());
386         assertEquals(savedSession.getId(), sessionPresenter4.getSessionId());
387 
388         ConferenceSession savedSession2 = getDataObjectService().save(savedSession);
389         assertEquals(4, savedSession2.getPresenters().size());
390     }
391 
392     /**
393      * Tests that linking is working for bi-directional collections
394      */
395     @Test
396     public void testBiDirectionalCollectionLinking() {
397         // we have a bidirectional collection relationship between ConferenceSession and SessionCoordinator so let's try that out
398         SessionCoordinator sc = new SessionCoordinator();
399         List<ConferenceSession> sessions = new ArrayList<ConferenceSession>();
400         ConferenceSession session = new ConferenceSession();
401         sessions.add(session);
402         sc.setAltCoordinatedSessions1(sessions);
403 
404         // before we link, we should be missing the relationship from ConferenceSession back to it's SessionCoordinator
405         assertNull(session.getAltCoordinator1());
406 
407         // first let's link without anything in our change list, it should not perform the linking
408         referenceLinker.linkChanges(sc, Sets.<String>newHashSet());
409 
410         // now let's link with the changes, should work if we just specify the "accounts" path
411         referenceLinker.linkChanges(sc, Sets.newHashSet("altCoordinatedSessions1"));
412 
413         // assert that our account now points back to it's account manager
414         assertEquals(sc, session.getAltCoordinator1());
415 
416         // now let's try linking using an indexed-based path of "altCoordinatedSessions1[0]", we will (of course) need to reset our
417         // session object first
418         session.setAltCoordinator1(null);
419 
420         // first, if we link with an invalid index, it should do nothing
421         referenceLinker.linkChanges(sc, Sets.newHashSet("altCoordinatedSessions1[1]"));
422         assertNull(session.getAltCoordinator1());
423 
424         referenceLinker.linkChanges(sc, Sets.newHashSet("altCoordinatedSessions1[0]"));
425         assertEquals(sc, session.getAltCoordinator1());
426 
427     }
428 
429     private DataObjectService getDataObjectService() {
430         return KradDataServiceLocator.getDataObjectService();
431     }
432 
433 }