View Javadoc
1   /*
2    * #%L
3    * ToPIA :: Service Replication
4    * $Id$
5    * $HeadURL$
6    * %%
7    * Copyright (C) 2004 - 2014 CodeLutin
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as 
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Lesser Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * #L%
23   */
24  
25  package org.nuiton.topia.replication;
26  
27  import org.apache.commons.logging.Log;
28  import org.junit.Assert;
29  import org.nuiton.i18n.I18n;
30  import org.nuiton.topia.TopiaContext;
31  import org.nuiton.topia.TopiaException;
32  import org.nuiton.topia.framework.TopiaContextImplementor;
33  import org.nuiton.topia.persistence.TopiaDAO;
34  import org.nuiton.topia.persistence.TopiaEntity;
35  import org.nuiton.topia.persistence.TopiaEntityEnum;
36  import org.nuiton.topia.persistence.util.EntityOperator;
37  import org.nuiton.topia.persistence.util.EntityOperatorStore;
38  import org.nuiton.topia.persistence.util.TopiaEntityHelper;
39  import org.nuiton.topia.replication.model.ReplicationModel;
40  import org.nuiton.topia.replication.model.ReplicationNode;
41  import org.nuiton.topia.replication.model.ReplicationOperationDef;
42  
43  import java.io.File;
44  import java.util.Arrays;
45  import java.util.Collection;
46  import java.util.Collections;
47  import java.util.Date;
48  import java.util.HashSet;
49  import java.util.Iterator;
50  import java.util.List;
51  import java.util.Locale;
52  import java.util.Set;
53  
54  /**
55   * TopiaReplicationServiceImplTest.
56   * 
57   * Created: 07 jun. 09 17:14:22
58   *
59   * @author tchemit &lt;chemit@codelutin.com&gt;
60   * @version $Id$
61   * @since 2.2.0
62   */
63  public abstract class AbstractTopiaReplicationServiceTest extends Assert {
64  
65      static protected TopiaContext context;
66  
67      static protected TopiaContext ctxt;
68  
69      protected TopiaContextImplementor dstCtxt;
70  
71      protected TopiaReplicationService service;
72  
73      protected ReplicationModel model;
74  
75      static protected boolean init;
76  
77      static private Long testsTimeStamp;
78  
79      static private File testsBasedir;
80  
81      static private final String TEST_BASEDIR = "target%1$ssurefire-tests%1$s%2$td_%2$tm_%2$tY%1$s%2$tH_%2$tM_%2$tS";
82  
83      public static void after() throws Exception {
84          if (context != null && !context.isClosed()) {
85              try {
86                  context.closeContext();
87              } catch (TopiaException e) {
88                  // cela peut arriver si on demande la fermeture dans un thread
89                  // ailleurs...
90              }
91          }
92          init = false;
93      }
94  
95      public void setUp() throws Exception {
96  
97          if (!init) {
98  
99              I18n.setDefaultLocale(Locale.FRANCE);
100 
101             try {
102                 context = createDb("source");
103             } catch (Exception e) {
104                 getLog().error("could not create db source.", e);
105                 throw e;
106             }
107             init = true;
108         }
109 
110         ctxt = context.beginTransaction();
111 
112         service = context.getService(TopiaReplicationService.class);
113     }
114 
115     public void tearDown() throws Exception {
116         if (ctxt != null) {
117             ctxt.rollbackTransaction();
118             ctxt.closeContext();
119             ctxt = null;
120         }
121         service = null;
122     }
123 
124 
125     protected TopiaReplicationModelBuilder getModelBuilder() {
126         return service.getModelBuilder();
127     }
128 
129     protected abstract TopiaContext createDb2(String name) throws Exception;
130 
131     protected abstract TopiaContext createDb(String name) throws Exception;
132 
133     protected TopiaContext createReplicateDb(Object contract) throws Exception {
134         TopiaContext rootCtxt = createDb2(contract.toString() + dbCounter++);
135         return rootCtxt;
136     }
137 
138     protected abstract TopiaEntityEnum[] getContracts();
139 
140     protected abstract Log getLog();
141 
142     protected <E extends TopiaEntity> E update(E e) throws TopiaException {
143         return (E) ctxt.findByTopiaId(e.getTopiaId());
144     }
145 
146     /**
147      * Test of detectTypes method, of class ReplicationServiceImplementor.
148      *
149      * @throws Exception if any error
150      */
151     public void testDetectTypes() throws Exception {
152     }
153 
154     /**
155      * Test of getOperation method, of class ReplicationServiceImplementor.
156      *
157      * @throws Exception if any error
158      */
159     public void testGetOperation() throws Exception {
160     }
161 
162     /**
163      * Test of detectAssociations method, of class ReplicationModel.
164      *
165      * @throws Exception if any error
166      */
167     public void testDetectAssociations() throws Exception {
168     }
169 
170     /**
171      * Test of detectDirectDependencies method, of class ReplicationModel.
172      *
173      * @throws Exception if any error
174      */
175     public void testDetectDirectDependencies() throws Exception {
176     }
177 
178     /**
179      * Test of detectShell method, of class ReplicationModel.
180      *
181      * @throws Exception if any error
182      */
183     public void testDetectShell() throws Exception {
184     }
185 
186     /**
187      * Test of detectDependencies method, of class ReplicationModel.
188      *
189      * @throws Exception if any error
190      */
191     public void testDetectDependencies() throws Exception {
192     }
193 
194     /**
195      * Test of detectObjectsToDettach method, of class ReplicationModel.
196      *
197      * @throws Exception if any error
198      */
199     public void testDetectObjectsToDettach() throws Exception {
200     }
201 
202     /**
203      * Test of detectOperations method, of class ReplicationModel.
204      *
205      * @throws Exception if any error
206      */
207     public void testDetectOperations() throws Exception {
208     }
209 
210     /**
211      * Test of doReplicate method, of class ReplicationService.
212      *
213      * @throws Exception if any error
214      */
215     public void testDoReplicate() throws Exception {
216     }
217 
218     protected void detectTypes(TopiaEntity entity, Object... expectedCouple) throws TopiaException {
219 
220         Set<?> detectTypes;
221 
222         detectTypes = service.getModelBuilder().detectTypes(context, getContracts(), entity.getTopiaId());
223         assertEquals("expected types : " +
224                      Arrays.toString(expectedCouple) +
225                      " but was " + detectTypes,
226                      expectedCouple.length, detectTypes.size());
227         for (Object o : expectedCouple) {
228             assertTrue(detectTypes.contains(o));
229         }
230     }
231 
232     protected void getOperation(Class<? extends TopiaReplicationOperation> operationClass, boolean shouldExist) throws TopiaException {
233         TopiaReplicationOperation operation = getModelBuilder().getOperationProvider().getOperation(operationClass);
234         assertEquals(shouldExist, operation != null);
235     }
236 
237     protected void detectAssociations(TopiaEntity entity,
238                                       Object... expectedCouple)
239             throws TopiaException {
240 
241         createModel(entity);
242         model.detectAssociations();
243 
244         assertEquals(0, expectedCouple.length % 2);
245 
246         for (int i = 0, j = expectedCouple.length / 2; i < j; i++) {
247             TopiaEntityEnum src = (TopiaEntityEnum) expectedCouple[2 * i];
248             String name = (String) expectedCouple[2 * i + 1];
249             ReplicationNode nodeSrc = model.getNode(src);
250             assertNotNull("association " + name + " not found", nodeSrc);
251             assertTrue(nodeSrc.hasAssociation());
252             assertTrue(nodeSrc.getAssociations().containsKey(name));
253         }
254     }
255 
256     protected void detectDirectDependencies(TopiaEntity entity,
257                                             Object... expectedCouple)
258             throws TopiaException {
259 
260         createModel(entity);
261         model.detectDirectDependencies();
262 
263         assertEquals(0, expectedCouple.length % 2);
264 
265         for (int i = 0, j = expectedCouple.length / 2; i < j; i++) {
266             TopiaEntityEnum src = (TopiaEntityEnum) expectedCouple[2 * i];
267             String name = (String) expectedCouple[2 * i + 1];
268             ReplicationNode nodeSrc = model.getNode(src);
269             assertTrue(nodeSrc + " should have dependency but was not!", nodeSrc.hasDependency());
270             assertTrue(nodeSrc + " should contain dependency " + name + "but was not! (" + nodeSrc.getDependencies() + ")", nodeSrc.getDependencies().containsKey(name));
271         }
272     }
273 
274     protected void detectShell(TopiaEntity entity,
275                                TopiaEntityEnum... expected) throws
276             TopiaException {
277         Set<ReplicationNode> shell;
278 
279         createModel(entity);
280         model.detectAssociations();
281         model.detectDirectDependencies();
282         model.detectShell();
283 
284         TopiaEntityEnum c = TopiaEntityHelper.getEntityEnum(
285                 entity.getClass(), getContracts());
286         assertNotNull(c);
287         shell = model.getNode(c).getShell();
288         assertEquals(
289                 "expected shell : " + Arrays.toString(expected) + ", but was " +
290                 shell, expected.length, shell.size());
291 
292         for (int i = 0, j = expected.length; i < j; i++) {
293             TopiaEntityEnum type = expected[i];
294             ReplicationNode node = model.getNode(type);
295             assertTrue(shell.contains(node));
296             assertEquals(type, node.getContract());
297         }
298     }
299 
300     protected void detectDependencies(
301             TopiaEntity entity,
302             TopiaEntityEnum[]... expected) throws TopiaException {
303 
304         createModel(entity);
305         model.detectAssociations();
306         model.detectDirectDependencies();
307         model.detectShell();
308         model.detectDependencies();
309         List<ReplicationNode> dependencies = model.getOrder();
310 
311         int i = 0;
312         for (ReplicationNode level : dependencies) {
313             getLog().info("level " + level + " = " + level);
314         }
315 
316 //        assertEquals("expected  " + expected.length + " levels but had " + dependencies.size(), expected.length, dependencies.size());
317 //
318 //        Iterator<List<ReplicationNode>> order = dependencies.iterator();
319 //        if (entity != null) {
320 //            getLog().info("for " + entity.getTopiaId());
321 //        }
322 //        int index = 0;
323 //        for (TopiaEntityEnum[] expectedLevel : expected) {
324 //
325 //            List<ReplicationNode> next = order.next();
326 //            getLog().info("level " + (index++) + " : " + next);
327 //            for (TopiaEntityEnum ee : expectedLevel) {
328 //                ReplicationNode expectedNode = model.getNode(ee);
329 //
330 //                assertTrue("should have contains node " + expectedNode, next.contains(expectedNode));
331 //            }
332 //
333 //        }
334     }
335 
336     protected void detectObjectsToDettach(TopiaEntity entity, Object... expected) throws TopiaException {
337 
338         assertEquals(0, expected.length % 2);
339 
340         createModel(entity);
341         model.detectAssociations();
342         model.detectDirectDependencies();
343         model.detectShell();
344         model.detectDependencies();
345         model.detectObjectsToDettach();
346         Set<ReplicationNode> nodes = new HashSet<ReplicationNode>();
347 
348         for (int i = 0, j = expected.length / 2; i < j; i++) {
349             TopiaEntityEnum e = (TopiaEntityEnum) expected[2 * i];
350             ReplicationNode node = model.getNode(e);
351             String[] ids = (String[]) expected[2 * i + 1];
352             assertEquals(ids.length > 0, node.hasAssociationsToDettach());
353             for (String id : ids) {
354                 assertTrue(node.getAssociationsToDettach().contains(id));
355             }
356             nodes.add(node);
357         }
358 
359         for (ReplicationNode node : model.getNodes()) {
360             if (!nodes.contains(node)) {
361                 // on verifie bien qu'il n' y a pas d'associations dettachee
362                 assertFalse(node.hasAssociationsToDettach());
363             }
364         }
365 
366     }
367 
368     protected void detectOperations(TopiaEntity entity, Object... expected) throws TopiaException {
369 
370         assertEquals(0, expected.length % 2);
371 
372         if (entity == null) {
373             prepareModel();
374         } else {
375             prepareModel(entity.getTopiaId());
376         }
377 //        createModel(entity);
378 //        model.detectAssociations();
379 //        model.detectDirectDependencies();
380 //        model.detectShell();
381 //        model.detectDependencies();
382 //        model.detectObjectsToDettach();
383 //        model.detectOperations();
384 
385         if (getLog().isInfoEnabled()) {
386             getLog().info("==========================================================================");
387             if (entity == null) {
388 
389                 getLog().info("resume of operations for all ");
390             } else {
391                 getLog().info("resume of operations for entity " + entity.getTopiaId());
392             }
393 
394             for (ReplicationNode node : model.getOrder()) {
395                 ReplicationOperationDef[] operations = node.getOperations();
396                 for (ReplicationOperationDef op : operations) {
397                     getLog().info("[" + node + "] : operation " + op);
398                 }
399             }
400             getLog().info("==========================================================================");
401         }
402     }
403 
404     private static int dbCounter;
405 
406     protected void doReplicate(TopiaEntityEnum contract,
407                                TopiaEntity... entity) throws Exception {
408 
409         TopiaContext rootCtxt = createReplicateDb("doReplicate_" + contract);
410 
411         List<String> ids = TopiaEntityHelper.getTopiaIdList(Arrays.asList(entity));
412         getLog().info("entity " + ids);
413 
414         prepareModel(ids.toArray(new String[ids.size()]));
415 
416         dstCtxt = (TopiaContextImplementor) rootCtxt;
417 
418         service.doReplicate(model, dstCtxt);
419 
420         //dstCtxt.closeContext();
421 
422         if (entity.length == 0) {
423 
424             return;
425         }
426         dstCtxt = (TopiaContextImplementor) rootCtxt.beginTransaction();
427 
428         for (TopiaEntity e : entity) {
429             TopiaEntity actual = dstCtxt.findByTopiaId(e.getTopiaId());
430             assertNotNull(actual);
431             assertEquals(e, actual);
432         }
433 
434         dstCtxt.closeContext();
435 
436         dstCtxt = (TopiaContextImplementor) rootCtxt;
437     }
438 
439     protected void doReplicateAll() throws Exception {
440 
441         TopiaContext rootCtxt = createReplicateDb("doReplicateAll");
442 
443         prepareModelAll();
444 
445         dstCtxt = (TopiaContextImplementor) rootCtxt;
446 
447         service.doReplicate(model, dstCtxt);
448 
449         TopiaContextImplementor ctxt2 = (TopiaContextImplementor) ctxt;
450         dstCtxt = (TopiaContextImplementor) rootCtxt.beginTransaction();
451 
452         assertDbEquals(model.getContracts(), (TopiaContextImplementor) ctxt, ctxt2);
453 
454         dstCtxt.closeContext();
455 
456         dstCtxt = (TopiaContextImplementor) rootCtxt;
457     }
458 
459     protected void doReplicateWithComputedOrder(TopiaEntity... entity) throws Exception {
460 
461         TopiaContext rootCtxt = createReplicateDb("doReplicateWithComputedOrder");
462 
463         List<String> ids = TopiaEntityHelper.getTopiaIdList(Arrays.asList(entity));
464 
465         prepareModelWithComputedOrder(ids.toArray(new String[ids.size()]));
466 
467         dstCtxt = (TopiaContextImplementor) rootCtxt;
468 
469         service.doReplicate(model, dstCtxt);
470 
471         getLog().info("replication is done for " + Arrays.toString(entity) + ", will verify data...");
472 
473         TopiaContextImplementor ctxt2 = (TopiaContextImplementor) ctxt;
474         dstCtxt = (TopiaContextImplementor) rootCtxt.beginTransaction();
475 
476         assertDbEquals(model.getContracts(), (TopiaContextImplementor) ctxt, ctxt2);
477 
478         dstCtxt.closeContext();
479 
480         dstCtxt = (TopiaContextImplementor) rootCtxt;
481     }
482 
483     protected void assertDbEquals(TopiaEntityEnum[] contracts,
484                                   TopiaContextImplementor ctxt,
485                                   TopiaContextImplementor ctxt2) throws TopiaException {
486         Set<String> ids = new HashSet<String>();
487 
488         if (getLog().isInfoEnabled()) {
489             getLog().info("will verify db for contracts " + Arrays.toString(contracts));
490         }
491         for (TopiaEntityEnum c : contracts) {
492             if (getLog().isDebugEnabled()) {
493                 getLog().debug("verify for contract " + c);
494             }
495             TopiaDAO<? extends TopiaEntity> daoSrc = ctxt.getDAO(c.getContract());
496             TopiaDAO<? extends TopiaEntity> daoDst = ctxt2.getDAO(c.getContract());
497             long nbSrc = daoSrc.count();
498             long nbDst = daoDst.count();
499             assertEquals("le nombres d'entites de type " + c + " devrait etre " + nbSrc + " mais est " + nbDst, nbSrc, nbDst);
500             List<String> idsSrc = daoSrc.findAllIds();
501             List<String> idsDst = daoDst.findAllIds();
502             Collections.sort(idsSrc);
503             Collections.sort(idsDst);
504             assertEquals(idsSrc, idsDst);
505             for (String id : idsSrc) {
506                 if (getLog().isDebugEnabled()) {
507                     getLog().debug("verify for entity " + id);
508                 }
509                 TopiaEntity eSrc = daoSrc.findByTopiaId(id);
510                 TopiaEntity eDst = daoDst.findByTopiaId(id);
511                 assertEquals(eSrc, eDst);
512                 assertEntityEquals(eSrc, eDst, ids);
513             }
514         }
515     }
516 
517     protected void assertEntityEquals(TopiaEntity expected,
518                                       TopiaEntity actual,
519                                       Set<String> treated) {
520         if (treated == null) {
521             treated = new HashSet<String>();
522         }
523         if (treated.contains(actual.getTopiaId())) {
524             return;
525         }
526         if (getLog().isDebugEnabled()) {
527             getLog().debug(expected);
528         }
529         assertEquals(actual.getTopiaId(), expected.getTopiaId());
530         treated.add(actual.getTopiaId());
531         if (getLog().isDebugEnabled()) {
532             getLog().debug("expected : " + expected + " / actual " + actual);
533         }
534         TopiaEntityEnum contract = TopiaEntityHelper.getEntityEnum(expected.getClass(), getContracts());
535         if (contract == null) {
536             // this type of entity in not dealed here...
537             getLog().debug("untested property type " + expected.getClass());
538             return;
539         }
540         Assert.assertNotNull(
541                 "contract not found for " + expected.getClass() + " in " +
542                 Arrays.toString(getContracts()), contract);
543         EntityOperator<TopiaEntity> operator = EntityOperatorStore.getOperator(contract);
544         List<String> associationProperties = operator.getAssociationProperties();
545         for (String name : associationProperties) {
546             if (getLog().isDebugEnabled()) {
547                 getLog().debug("association " + name);
548             }
549             if (operator.isChildEmpty(name, expected)) {
550                 assertTrue("l'association " + name + " devrait etre vide mais possede " + operator.sizeChild(name, actual) + " entrees", operator.isChildEmpty(name, actual));
551             } else {
552                 assertFalse("l'association " + name + " devrait posseder " + operator.isChildEmpty(name, expected) + " mais est vide", operator.isChildEmpty(name, actual));
553 
554             }
555             assertEquals(operator.isChildEmpty(name, actual), operator.isChildEmpty(name, expected));
556 
557             Class<?> type = operator.getAssociationPropertyType(name);
558             Collection<?> src = (Collection<?>) operator.get(name, expected);
559             Collection<?> dst = (Collection<?>) operator.get(name, actual);
560 //            assertEquals(src, dst);
561             Iterator<?> itrSrc = src.iterator();
562             Iterator<?> itrDst = dst.iterator();
563             while (itrSrc.hasNext()) {
564                 if (TopiaEntity.class.isAssignableFrom(type)) {
565                     assertEntityEquals((TopiaEntity) itrSrc.next(), (TopiaEntity) itrDst.next(), treated);
566                 } else {
567                     assertEquals(itrSrc.next(), itrDst.next());
568                 }
569             }
570         }
571 
572         for (String name : operator.getProperties()) {
573             if (getLog().isDebugEnabled()) {
574                 getLog().debug("dependency " + name);
575             }
576             if (associationProperties.contains(name)) {
577                 // deja traite au dessus
578                 continue;
579             }
580             Class<?> type = operator.getPropertyType(name);
581             Object src = operator.get(name, expected);
582             Object dst = operator.get(name, actual);
583             assertFalse(src == null && dst != null);
584             assertFalse(src != null && dst == null);
585             if (src == null) {
586                 continue;
587             }
588             if (TopiaEntity.class.isAssignableFrom(type)) {
589                 assertEntityEquals((TopiaEntity) src, (TopiaEntity) dst, treated);
590             } else {
591                 assertEquals(src, dst);
592             }
593         }
594     }
595 
596     @Deprecated
597     protected void createUnsupportedBeforeOperation(TopiaEntityEnum contract,
598                                                     TopiaEntity entity,
599                                                     Class<? extends TopiaReplicationOperation> operationClass,
600                                                     Object... parameters) throws Exception {
601 
602         getLog().info("entity " + entity.getTopiaId());
603         prepareModel(entity.getTopiaId());
604 
605         getModelBuilder().addBeforeOperation(model, contract, operationClass, parameters);
606         // on ne doit pas avoir le droit de creer cette operation
607         fail();
608     }
609 
610     @Deprecated
611     protected void createUnsupportedAfterOperation(
612             TopiaEntityEnum contract,
613             TopiaEntity entity,
614             Class<? extends TopiaReplicationOperation> operationClass,
615             Object... parameters) throws Exception {
616 
617         getLog().info("entity " + entity.getTopiaId());
618         prepareModel(entity.getTopiaId());
619 //        model = service.createModel(getContracts());
620 //        model.detectDirectDependencies();
621         getModelBuilder().addAfterOperation(model, contract, operationClass, parameters);
622         // on ne doit pas avoir le droit de creer cette operation
623         fail();
624     }
625 
626     protected void createSupportedBeforeOperation(TopiaEntityEnum contract,
627                                                     TopiaEntity entity,
628                                                     Class<? extends TopiaReplicationOperation> operationClass,
629                                                     Object... parameters) throws Exception {
630 
631         getLog().info("entity " + entity.getTopiaId());
632         prepareModel(entity.getTopiaId());
633 
634         getModelBuilder().addBeforeOperation(model, contract, operationClass, parameters);
635         // on doit avoir le droit de creer cette operation
636         Assert.assertTrue(true);
637     }
638 
639     protected void createSupportedAfterOperation(
640             TopiaEntityEnum contract,
641             TopiaEntity entity,
642             Class<? extends TopiaReplicationOperation> operationClass,
643             Object... parameters) throws Exception {
644 
645         getLog().info("entity " + entity.getTopiaId());
646         prepareModel(entity.getTopiaId());
647 //        model = service.createModel(getContracts());
648 //        model.detectDirectDependencies();
649         getModelBuilder().addAfterOperation(model, contract, operationClass, parameters);
650         // on doit avoir le droit de creer cette operation
651         Assert.assertTrue(true);
652     }
653 
654     protected Long getTestsTimeStamp() {
655         if (testsTimeStamp == null) {
656             testsTimeStamp = System.currentTimeMillis();
657             getLog().info("tests timestamp : " + testsTimeStamp);
658         }
659         return testsTimeStamp;
660     }
661 
662     protected File getTestDir(Class<?> testClass) {
663         if (testsBasedir == null) {
664             String tmp = System.getProperty("basedir");
665             if (tmp == null) {
666                 tmp = new File("").getAbsolutePath();
667             }
668             String name = String.format(TEST_BASEDIR, File.separator, new Date(getTestsTimeStamp()));
669             testsBasedir = new File(new File(tmp), name);
670             getLog().info("tests basedir   : " + testsBasedir);
671         }
672         return new File(testsBasedir, testClass.getSimpleName());
673     }
674 
675     protected void createModel(TopiaEntity entity) throws TopiaException {
676         model = getModelBuilder().createModel(context,
677                                               getContracts(),
678                                               true,
679                                               entity.getTopiaId()
680         );
681     }
682 
683     protected void prepareModel(String... ids) throws TopiaException {
684         model = service.prepare(getContracts(), true, ids);
685     }
686 
687     protected void prepareModelAll() throws TopiaException {
688         model = service.prepareForAll(getContracts());
689     }
690 
691     protected void prepareModelWithComputedOrder(String... ids) throws TopiaException {
692         model = service.prepare(getContracts(), false, ids);
693     }
694 }