KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > speedo > runtime > fetchgroup > TestRefreshFetchGroup


1 /**
2  * Speedo: an implementation of JDO compliant personality on top of JORM generic
3  * I/O sub-system.
4  * Copyright (C) 2001-2004 France Telecom R&D
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  *
21  *
22  * Contact: speedo@objectweb.org
23  *
24  */

25
26 package org.objectweb.speedo.runtime.fetchgroup;
27
28
29 import java.util.Collection JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.Set JavaDoc;
33
34 import javax.jdo.FetchPlan;
35 import javax.jdo.JDOException;
36 import javax.jdo.PersistenceManager;
37 import javax.jdo.Query;
38
39 import junit.framework.Assert;
40
41 import org.objectweb.speedo.SpeedoTestHelper;
42 import org.objectweb.speedo.api.ExceptionHelper;
43 import org.objectweb.speedo.pm.api.ProxyManager;
44 import org.objectweb.speedo.pobjects.fetchgroup.Address;
45 import org.objectweb.speedo.pobjects.fetchgroup.Country;
46 import org.objectweb.speedo.pobjects.fetchgroup.EdgeWeight;
47 import org.objectweb.speedo.pobjects.fetchgroup.Node;
48 import org.objectweb.speedo.pobjects.fetchgroup.Person;
49 import org.objectweb.util.monolog.api.BasicLevel;
50
51 /**
52  *
53  * @author Y.Bersihand
54  */

55 public class TestRefreshFetchGroup extends SpeedoTestHelper {
56
57     public TestRefreshFetchGroup(String JavaDoc s) {
58         super(s);
59     }
60
61     protected String JavaDoc getLoggerName() {
62         return LOG_NAME + ".rt.fetchgroup.TestRefreshFetchGroup";
63     }
64     
65     /**
66      * Test the refresh with the detail fetch group :
67      * test the definition of a fetch-group in the jdo file with a a.b.c field
68      * <field name="a.b.c">
69      */

70     public void testRefreshReference() {
71         logger.log(BasicLevel.DEBUG, "***************testRefreshReference*****************");
72         Country country = new Country("it","Italie");
73         Address address = new Address("Rue Spiaggi", "Milan", country);
74         Person parent = new Person();
75         parent.setName("Del Piero Joel");
76         parent.setAge(32);
77         parent.setAddress(address);
78         Person child1 = new Person("Del Piero Sophie", address, null, 14);
79         Person child2 = new Person("Del Piero Mikael", address, null, 11);
80         Set JavaDoc children = new HashSet JavaDoc();
81         children.add(child1);
82         children.add(child2);
83         parent.setChildren(children);
84         PersistenceManager pm = pmf.getPersistenceManager();
85         try {
86             FetchPlan fp = pm.getFetchPlan();
87             fp.clearGroups();
88             fp.addGroup("detail").removeGroup("default");
89             pm.currentTransaction().begin();
90             logger.log(BasicLevel.DEBUG, "make persistent the person " + parent.toString());
91             pm.makePersistent(parent);
92             Object JavaDoc id = pm.getObjectId(parent);
93             pm.currentTransaction().commit();
94             logger.log(BasicLevel.DEBUG, "FG: " + fp.getGroups());
95         
96             pm.currentTransaction().begin();
97             //update the age and the address
98
parent.setAge(99);
99             Address newAddress = new Address("rue Ampere", "Rennes", new Country("bz", "Bretagne"));
100             parent.setAddress(newAddress);
101             pm.refresh(parent);
102             pm.currentTransaction().commit();
103             assertEquals(32, parent.getAge());
104             assertEquals(address.getCity(), parent.getAddress().getCity());
105             assertEquals(address.getStreet(), parent.getAddress().getStreet());
106         } catch(Exception JavaDoc e) {
107             if (pm.currentTransaction().isActive())
108                 pm.currentTransaction().rollback();
109             fail(e.getMessage());
110         } finally {
111             pm.close();
112         }
113     }
114     
115     /**
116      * Test the refresh with the detail+children-names fetch group:
117      * test the definition of a fetch-group in the jdo file with a a#element.b field
118      * <field name="a#element.b">
119      */

120     public void testRefreshArrayElement() {
121         logger.log(BasicLevel.DEBUG, "************testRefreshArrayElement**************");
122         Country country = new Country("be","Belgique");
123         Address address = new Address("Rue Anvers", "Bruges", country);
124         Person parent = new Person();
125         parent.setName("Dermuck Joel");
126         parent.setAge(32);
127         parent.setAddress(address);
128         Person child1 = new Person("Dermuck Sophie", address, null, 14);
129         Person child2 = new Person("Dermuck Mikael", address, null, 11);
130         Set JavaDoc children = new HashSet JavaDoc();
131         children.add(child1);
132         children.add(child2);
133         parent.setChildren(children);
134         
135         PersistenceManager pm = pmf.getPersistenceManager();
136         FetchPlan fp = pm.getFetchPlan();
137         fp.clearGroups();
138         fp.addGroup("detail+children-names").removeGroup("default");
139         pm.currentTransaction().begin();
140         logger.log(BasicLevel.DEBUG, "make persistent the person " + parent.toString());
141         pm.makePersistent(parent);
142         pm.currentTransaction().commit();
143         logger.log(BasicLevel.DEBUG, "FG: " + fp.getGroups());
144         try{
145             pm.currentTransaction().begin();
146             //update the first child age and address
147
Person p = (Person) parent.getChildren().iterator().next();
148             p.setAge(2000);
149             Address newAddress = new Address("Rue Refresh", "Moon", new Country("m", "mars"));
150             p.setAddress(newAddress);
151             pm.refresh(parent);
152         
153             Person ch = (Person) parent.getChildren().iterator().next();
154             pm.currentTransaction().commit();
155             assertEquals(14, ch.getAge());
156             assertEquals(address.getCity(), ch.getAddress().getCity());
157             assertEquals(address.getStreet(), ch.getAddress().getStreet());
158         } catch (Exception JavaDoc e) {
159             if (pm.currentTransaction().isActive())
160                 pm.currentTransaction().rollback();
161             fail(e.getMessage());
162         } finally {
163             pm.close();
164         }
165     }
166
167     
168     /**
169      * Test the refresh with the detail+children-list fetch group:
170      * test the definition of a fetch-group in the jdo file with a fetch-group attribute in a field
171      * <field name="a" fetch-group="fg"/>
172      */

173     public void testRefreshFetchGroupField() {
174         logger.log(BasicLevel.DEBUG, "************testRefreshFetchGroupField**************");
175         Country country = new Country("us","Etats-Unis");
176         Address address = new Address("Rue Enclif", "San Diego", country);
177         Person parent = new Person();
178         parent.setName("Smith Joel");
179         parent.setAge(32);
180         parent.setAddress(address);
181         Person child1 = new Person("Smith Sofia", address, null, 14);
182         Person child2 = new Person("Smith Michael", address, null, 11);
183         Set JavaDoc children = new HashSet JavaDoc();
184         children.add(child1);
185         children.add(child2);
186         parent.setChildren(children);
187         
188         PersistenceManager pm = pmf.getPersistenceManager();
189         FetchPlan fp = pm.getFetchPlan();
190         fp.clearGroups();
191         fp.addGroup("detail+children-list").removeGroup("default");
192         pm.currentTransaction().begin();
193         logger.log(BasicLevel.DEBUG, "make persistent the person " + parent.toString());
194         pm.makePersistent(parent);
195         pm.currentTransaction().commit();
196         logger.log(BasicLevel.DEBUG, "FG: " + fp.getGroups());
197         try{
198             pm.currentTransaction().begin();
199             // update the first child age and address
200
Person p = (Person) parent.getChildren().iterator().next();
201             p.setAge(2000);
202             Address newAddress = new Address("Rue Far", "Moon", new Country("n", "neptune"));
203             p.setAddress(newAddress);
204             pm.refresh(parent);
205         
206             pm.currentTransaction().commit();
207             Person ch = (Person) parent.getChildren().iterator().next();
208             int expectedAge = 0;
209             if (ch.getName().equals(child1.getName())) {
210                 expectedAge = child1.getAge();
211             } else {
212                 expectedAge = child2.getAge();
213             }
214             assertEquals(expectedAge, ch.getAge());
215             assertEquals(address.getCity(), ch.getAddress().getCity());
216             assertEquals(address.getStreet(), ch.getAddress().getStreet());
217         } catch (Exception JavaDoc e) {
218             if (pm.currentTransaction().isActive())
219                 pm.currentTransaction().rollback();
220             fail(e.getMessage());
221         } finally {
222             pm.close();
223         }
224         
225     }
226
227     /**
228      * Test the refresh with the detailChildren fetch group:
229      * test the definition of a fetch-group in the jdo file with a depth attribute in a field
230      * for recursive reference
231      * <field name="a" depth="X"/>
232      */

233     public void testRefreshRecursiveDepth() {
234         logger.log(BasicLevel.DEBUG, "************testRefreshRecursiveDepth**************");
235         Country country = new Country("sp","Espagne");
236         Address address = new Address("Rue Rio", "Santander", country);
237         Person parent = new Person();
238         parent.setName("Casillas Joel");
239         parent.setAge(63);
240         parent.setAddress(address);
241         Person child1 = new Person("Casillas Sofia", address, null, 30);
242         Person child2 = new Person("Casillas Michael", address, null, 40);
243         Set JavaDoc children = new HashSet JavaDoc();
244         children.add(child1);
245         children.add(child2);
246         parent.setChildren(children);
247         Person child11 = new Person("Casillas Maria", address, null, 14);
248         Person child21 = new Person("Casillas Juan", address, null, 11);
249         Set JavaDoc children1 = new HashSet JavaDoc();
250         children1.add(child11);
251         Set JavaDoc children2 = new HashSet JavaDoc();
252         children2.add(child21);
253         child1.setChildren(children1);
254         child2.setChildren(children2);
255         
256         PersistenceManager pm = pmf.getPersistenceManager();
257         FetchPlan fp = pm.getFetchPlan();
258         fp.clearGroups();
259         fp.addGroup("detailChildren").removeGroup("default");
260         pm.currentTransaction().begin();
261         logger.log(BasicLevel.DEBUG, "make persistent the person " + parent.toString());
262         pm.makePersistent(parent);
263         pm.currentTransaction().commit();
264         logger.log(BasicLevel.DEBUG, "FG: " + fp.getGroups());
265         try{
266             Address a = new Address("Rue So", "Moon", new Country("k", "Krypton"));
267         
268             pm.currentTransaction().begin();
269             Person p = (Person) parent.getChildren().iterator().next();
270             p.setAge(2000);
271             p.setAddress(a);
272             Person pc = (Person) p.getChildren().iterator().next();
273             pc.setAge(3000);
274             pc.setAddress(a);
275             ((ProxyManager)pm).refresh(parent);
276             logger.log(BasicLevel.DEBUG, "Warning: the child of child will not be refreshed.");
277             pm.currentTransaction().commit();
278             
279             Person ch = (Person) parent.getChildren().iterator().next();
280             int expectedAge = 0;
281             if (ch.getName().equals(child1.getName())) {
282                 expectedAge = child1.getAge();
283             } else {
284                 expectedAge = child2.getAge();
285             }
286             assertEquals(expectedAge, ch.getAge());
287             assertEquals(address.getCity(), ch.getAddress().getCity());
288             ch = (Person) ch.getChildren().iterator().next();
289             assertEquals(3000, ch.getAge());
290             assertEquals("Moon", ch.getAddress().getCity());
291         } catch (Exception JavaDoc e) {
292             if (pm.currentTransaction().isActive())
293                 pm.currentTransaction().rollback();
294             fail(e.getMessage());
295         } finally {
296             pm.close();
297         }
298     }
299     
300     /**
301      * Test the refresh with the detailChildren fetch group:
302      * test the definition of a fetch-group in the jdo file with a depth attribute defined twice for a field
303      * <field name="a" depth="X"/>
304      */

305     public void testRefreshDoubleDepth() {
306         logger.log(BasicLevel.DEBUG, "************testRefreshDoubleDepth**************");
307         Country country = new Country("bl","Belarus");
308         Address address = new Address("Rue Kaloc", "Minsk", country);
309         Person parent = new Person();
310         parent.setName("Castuk Joel");
311         parent.setAge(63);
312         parent.setAddress(address);
313         Person child1 = new Person("Castuk Sofia", address, null, 40);
314         Person child2 = new Person("Castuk Michael", address, null, 40);
315         Set JavaDoc children = new HashSet JavaDoc();
316         children.add(child1);
317         children.add(child2);
318         parent.setChildren(children);
319         Person child11 = new Person("Castuk Maria", address, null, 11);
320         Person child21 = new Person("Castuk Juan", address, null, 11);
321         Set JavaDoc children1 = new HashSet JavaDoc();
322         children1.add(child11);
323         Set JavaDoc children2 = new HashSet JavaDoc();
324         children2.add(child21);
325         child1.setChildren(children1);
326         child2.setChildren(children2);
327         
328         PersistenceManager pm = pmf.getPersistenceManager();
329         FetchPlan fp = pm.getFetchPlan();
330         fp.clearGroups();
331         fp.addGroup("detailChildren2").removeGroup("default");
332         pm.currentTransaction().begin();
333         logger.log(BasicLevel.DEBUG, "make persistent the person " + parent.toString());
334         pm.makePersistent(parent);
335         pm.currentTransaction().commit();
336         logger.log(BasicLevel.DEBUG, "FG: " + fp.getGroups());
337         try{
338             pm.currentTransaction().begin();
339             Address a = new Address("Rue Away", "Moon", new Country("pl", "Pluton"));
340             Person p = (Person) parent.getChildren().iterator().next();
341             p.setAge(2000);
342             p.setAddress(a);
343             Person pc = (Person) p.getChildren().iterator().next();
344             pc.setAge(3000);
345             pc.setAddress(a);
346             ((ProxyManager)pm).refresh(parent);
347             pm.currentTransaction().commit();
348
349             Person ch = (Person) parent.getChildren().iterator().next();
350             assertEquals(40, ch.getAge());
351             assertEquals(address.getCity(), ch.getAddress().getCity());
352             ch = (Person) ch.getChildren().iterator().next();
353             assertEquals(11, ch.getAge());
354             assertEquals(address.getCity(), ch.getAddress().getCity());
355         } catch(Exception JavaDoc e) {
356             if (pm.currentTransaction().isActive())
357                 pm.currentTransaction().rollback();
358             fail(e.getMessage());
359         } finally {
360             pm.close();
361         }
362     }
363
364     /**
365      * Test the refresh with the detailChildren fetch group:
366      * test the definition of a fetch-group in the jdo file with a depth attribute defined twice for a field
367      * one of them is unlimited
368      * <field name="a" depth="X"/>
369      */

370     public void testRefreshDoubleDepthUnlimited() {
371         logger.log(BasicLevel.DEBUG, "************testRefreshDoubleDepthUnlimited**************");
372         
373         Country country = new Country("p","Portugal");
374         Address address = new Address("Rue Christiano", "Lisbonne", country);
375         Person grandParent = new Person();
376         grandParent.setName("Simoes Joel");
377         grandParent.setAge(90);
378         grandParent.setAddress(address);
379         Person parent1 = new Person("Simoes Sofia", address, null, 70);
380         Person parent2 = new Person("Simoes Michael", address, null, 70);
381         Set JavaDoc parent = new HashSet JavaDoc();
382         parent.add(parent1);
383         parent.add(parent2);
384         grandParent.setChildren(parent);
385         Person child1 = new Person("Simoes Maria", address, null, 40);
386         Person child2 = new Person("Simoes Juan", address, null, 40);
387         Set JavaDoc children1 = new HashSet JavaDoc();
388         children1.add(child1);
389         Set JavaDoc children2 = new HashSet JavaDoc();
390         children2.add(child2);
391         parent1.setChildren(children1);
392         parent2.setChildren(children2);
393         Person grandChild1 = new Person("Simoes Leia", address, null, 10);
394         Person grandChild2 = new Person("Simoes Carlos", address, null, 10);
395         Set JavaDoc grandChildren1 = new HashSet JavaDoc();
396         grandChildren1.add(grandChild1);
397         Set JavaDoc grandChildren2 = new HashSet JavaDoc();
398         grandChildren2.add(grandChild2);
399         child1.setChildren(grandChildren1);
400         child2.setChildren(grandChildren2);
401         
402         PersistenceManager pm = pmf.getPersistenceManager();
403         FetchPlan fp = pm.getFetchPlan();
404         fp.clearGroups();
405         fp.addGroup("detailChildren3").removeGroup("default");
406         pm.currentTransaction().begin();
407         logger.log(BasicLevel.DEBUG, "make persistent the person " + grandParent.toString());
408         pm.makePersistent(grandParent);
409         pm.currentTransaction().commit();
410         logger.log(BasicLevel.DEBUG, "FG: " + fp.getGroups());
411         
412         try {
413             pm.currentTransaction().begin();
414             Address a = new Address("Rue From", "Moon", new Country("sl", "Solarus"));
415             Person pParent = (Person) grandParent.getChildren().iterator().next();
416             pParent.setAge(2000);
417             pParent.setAddress(a);
418             Person pChild = (Person) pParent.getChildren().iterator().next();
419             pChild.setAge(3000);
420             pChild.setAddress(a);
421             Person pGrandChild = (Person) pChild.getChildren().iterator().next();
422             pGrandChild.setAge(4000);
423             pGrandChild.setAddress(a);
424             ((ProxyManager)pm).refresh(grandParent);
425             pm.currentTransaction().commit();
426             
427             Person cParent = (Person) grandParent.getChildren().iterator().next();
428             assertEquals(70, cParent.getAge());
429             assertEquals(address.getCity(), cParent.getAddress().getCity());
430             Person cChild = (Person) cParent.getChildren().iterator().next();
431             assertEquals(40, cChild.getAge());
432             assertEquals(address.getCity(), cChild.getAddress().getCity());
433             Person cGrandChild = (Person) cChild.getChildren().iterator().next();
434             assertEquals(10, cGrandChild.getAge());
435             assertEquals(address.getCity(), cGrandChild.getAddress().getCity());
436             
437         } catch(Exception JavaDoc e) {
438             if (pm.currentTransaction().isActive())
439                 pm.currentTransaction().rollback();
440             fail(e.getMessage());
441         } finally {
442             pm.close();
443         }
444     }
445     
446     /**
447      * Test the refresh with the keyValue fetchgroup:
448      * test the definition of a fetch-group with a map, refresh both keys and values
449      * <field name="map#key">
450      * <field name="map#value">
451      */

452     public void testRefreshMapKeyValue() {
453         logger.log(BasicLevel.DEBUG, "************testRefreshMapKeyValue**************");
454         Node n1 = new Node("n1");
455         Node n2 = new Node("n2");
456         Node n3 = new Node("n3");
457         Node n4 = new Node("n4");
458         Node n5 = new Node("n5");
459         
460         n1.addEdge(n2.getName(), new EdgeWeight(1));
461         n1.addEdge(n3.getName(), new EdgeWeight(2));
462         
463         n2.addEdge(n4.getName(), new EdgeWeight(7));
464         n2.addEdge(n5.getName(), new EdgeWeight(4));
465         
466         PersistenceManager pm = pmf.getPersistenceManager();
467         FetchPlan fp = pm.getFetchPlan();
468         fp.clearGroups();
469         fp.addGroup("keyValue").removeGroup("default");
470         pm.currentTransaction().begin();
471         logger.log(BasicLevel.DEBUG, "make persistent the nodes " + n1.toString() + ", " + n2.toString()
472                 + ", " + n3.toString() + ", " + n4.toString() + ", " + n5.toString());
473         pm.makePersistent(n1);
474         pm.makePersistent(n2);
475         pm.makePersistent(n3);
476         pm.makePersistent(n4);
477         pm.makePersistent(n5);
478         pm.currentTransaction().commit();
479         
480         FetchPlan f = pm.getFetchPlan();
481         logger.log(BasicLevel.DEBUG, "FG: " + f.getGroups());
482         
483         try{
484             pm.currentTransaction().begin();
485             String JavaDoc nodeName = (String JavaDoc) n1.getEdges().keySet().iterator().next();
486             EdgeWeight ew = (EdgeWeight) n1.getEdges().get(nodeName);
487             ew.setWeight(3000);
488             ((ProxyManager)pm).refresh(n1);
489             pm.currentTransaction().commit();
490             assertEquals(2, ew.getWeight());
491         } catch(Exception JavaDoc e) {
492             if (pm.currentTransaction().isActive())
493                 pm.currentTransaction().rollback();
494             fail(e.getMessage());
495         } finally {
496             pm.close();
497         }
498     }
499     
500     
501     /**
502      * Test the refresh with the keyOnly fetchgroup:
503      * test the definition of a fetch-group with a map, refresh only keys
504      * <field name="map#key">
505      */

506     public void testRefreshMapKeyOnly() {
507         logger.log(BasicLevel.DEBUG, "************testRefreshMapKeyOnly**************");
508     
509         Node n1 = new Node("n11");
510         Node n2 = new Node("n21");
511         Node n3 = new Node("n31");
512         Node n4 = new Node("n41");
513         Node n5 = new Node("n51");
514         
515         n1.addEdge(n2.getName(), new EdgeWeight(1));
516         n1.addEdge(n3.getName(), new EdgeWeight(2));
517         
518         n2.addEdge(n4.getName(), new EdgeWeight(7));
519         n2.addEdge(n5.getName(), new EdgeWeight(4));
520         
521         PersistenceManager pm = pmf.getPersistenceManager();
522         FetchPlan fp = pm.getFetchPlan();
523         fp.clearGroups();
524         fp.addGroup("keyOnly").removeGroup("default");
525         pm.currentTransaction().begin();
526         logger.log(BasicLevel.DEBUG, "make persistent the nodes " + n1.toString() + ", " + n2.toString()
527                 + ", " + n3.toString() + ", " + n4.toString() + ", " + n5.toString());
528         pm.makePersistent(n1);
529         pm.makePersistent(n2);
530         pm.makePersistent(n3);
531         pm.makePersistent(n4);
532         pm.makePersistent(n5);
533         pm.currentTransaction().commit();
534         
535         FetchPlan f = pm.getFetchPlan();
536         logger.log(BasicLevel.DEBUG, "FG: " + f.getGroups());
537         
538         try {
539             pm.currentTransaction().begin();
540             String JavaDoc nodeName = (String JavaDoc) n1.getEdges().keySet().iterator().next();
541             EdgeWeight ew = (EdgeWeight) n1.getEdges().get(nodeName);
542             ew.setWeight(3000);
543             ((ProxyManager)pm).refresh(n1);
544             pm.currentTransaction().commit();
545             assertEquals(3000, ew.getWeight());
546         } catch(Exception JavaDoc e) {
547             if (pm.currentTransaction().isActive())
548                 pm.currentTransaction().rollback();
549             fail(e.getMessage());
550         } finally {
551             pm.close();
552         }
553     }
554
555     public void testRemovingOfPersistentObject() {
556         PersistenceManager pm = pmf.getPersistenceManager();
557         try {
558             Class JavaDoc[] cs = new Class JavaDoc[]{Address.class, Country.class, Person.class, Node.class, EdgeWeight.class};
559             pm.currentTransaction().begin();
560             for(int i=0; i<cs.length; i++) {
561                 Query query = pm.newQuery(cs[i]);
562                 Collection JavaDoc col = (Collection JavaDoc) query.execute();
563                 Iterator JavaDoc it = col.iterator();
564                 while(it.hasNext()) {
565                     Object JavaDoc o = it.next();
566                     Assert.assertNotNull("null object in the query result"
567                         + cs[i].getName(), o);
568                     pm.deletePersistent(o);
569
570                 }
571                 query.close(col);
572             }
573             pm.currentTransaction().commit();
574         } catch (JDOException e) {
575             Exception JavaDoc ie = ExceptionHelper.getNested(e);
576             logger.log(BasicLevel.ERROR, "", ie);
577             fail(ie.getMessage());
578         } finally {
579             pm.close();
580         }
581     }
582 }
583
Popular Tags