KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > framework > engine > source > MemorySyncSource


1 /**
2  * Copyright (C) 2003-2005 Funambol
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18 package sync4j.framework.engine.source;
19
20 import java.util.Collection JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24
25 import java.security.Principal JavaDoc;
26 import java.sql.Timestamp JavaDoc;
27
28 import sync4j.framework.engine.SyncProperty;
29 import sync4j.framework.engine.SyncItemKey;
30 import sync4j.framework.engine.SyncItem;
31 import sync4j.framework.engine.SyncItemImpl;
32 import sync4j.framework.engine.SyncItemState;
33 import sync4j.framework.engine.source.SyncSourceException;
34 import sync4j.framework.engine.source.SyncSource;
35
36 /*
37  * This source represents a <i>SyncSource</i> where all items are kept in memory.
38  * A <i>MemorySyncSource</i> can be created passing in the constructor the name
39  * of the source and the items or creating an anonymous source with the default
40  * constructor and then initialize the source with <i>initialize(...)</i>.
41  * <p>
42  * <code>existingSyncItems</code> represents the items on the device.
43  * <p>
44  * <code>newSyncItems</code>, <code>updatedSyncItems</code> and
45  * <code>deletedSyncItems</code> are insted of the modifications reported with
46  * the last message.
47  *
48  * @author Stefano Fornari @ Funambol
49  *
50  * @version $Id: MemorySyncSource.java,v 1.17 2005/04/29 15:30:26 nichele Exp $
51  */

52 public class MemorySyncSource
53 extends AbstractSyncSource
54 implements SyncSource, CommittableSyncSource, java.io.Serializable JavaDoc {
55
56     // --------------------------------------------------------------- Constants
57

58     public static final String JavaDoc NAME = "MemorySyncSource";
59
60     // ----------------------------------------------------- Private data member
61

62     // the content of the SyncML client
63
private List JavaDoc existingSyncItems;
64     private List JavaDoc newSyncItems ;
65     private List JavaDoc updatedSyncItems ;
66     private List JavaDoc deletedSyncItems ;
67
68     private List JavaDoc allNewSyncItems ;
69     private List JavaDoc allUpdatedSyncItems ;
70     private List JavaDoc allDeletedSyncItems ;
71
72     /**
73      * LO SyncItem - not null if any LO is being retrieved
74      */

75     private SyncItem lo;
76
77     // ------------------------------------------------------------ Constructors
78

79     public MemorySyncSource() {
80         lo = null;
81     }
82
83     public MemorySyncSource(String JavaDoc name, String JavaDoc type, String JavaDoc sourceURI) {
84         super(name, type, sourceURI);
85         lo = null;
86         initialize(new ArrayList JavaDoc(), new ArrayList JavaDoc(), new ArrayList JavaDoc());
87
88         allNewSyncItems = new ArrayList JavaDoc();
89         allUpdatedSyncItems = new ArrayList JavaDoc();
90         allDeletedSyncItems = new ArrayList JavaDoc();
91
92         setExistingItems(new ArrayList JavaDoc());
93     }
94
95     public MemorySyncSource(String JavaDoc name ,
96                             List JavaDoc unchangedSyncItems,
97                             List JavaDoc deletedSyncItems ,
98                             List JavaDoc newSyncItems ) {
99       //
100
// The items are added so no need to clone the lists
101
//
102
super(name);
103       lo = null;
104       initialize(deletedSyncItems, newSyncItems, updatedSyncItems);
105       setExistingItems(new ArrayList JavaDoc());
106     }
107
108     // ---------------------------------------------------------- Public Methods
109

110     /**
111      * Sets the name of the source.
112      *
113      * @param name the new name
114      */

115     public void setName(String JavaDoc name) {
116         this.name = name;
117     }
118
119     /*
120     * @see SyncSource
121     */

122
123     public SyncItem setSyncItem(Principal JavaDoc principal, SyncItem syncItem)
124     throws SyncSourceException {
125         newSyncItems.remove (syncItem);
126         deletedSyncItems.add(syncItem);
127
128         updatedSyncItems.add(syncItem);
129
130         SyncItemKey mappedKey = syncItem.getMappedKey();
131         String JavaDoc mappedKeyValue = (mappedKey == null) ? null
132                                                     : mappedKey.getKeyAsString();
133
134         SyncItem newSyncItem = new SyncItemImpl(
135                                    this,
136                                    syncItem.getKey().getKeyAsString(),
137                                    mappedKeyValue,
138                                    SyncItemState.NEW
139                                );
140
141         newSyncItem.setProperties(syncItem.getProperties());
142
143         return newSyncItem;
144     }
145
146     /*
147     * @see SyncSource
148     */

149
150     public SyncItem[] setSyncItems(Principal JavaDoc principal, SyncItem[] syncItems)
151     throws SyncSourceException {
152         SyncItem[] ret = new SyncItem[syncItems.length];
153         for (int i=0; (i<syncItems.length); ++i) {
154             ret[i] = setSyncItem(principal, syncItems[i]);
155         } // next i
156

157         return ret;
158     }
159
160     /*
161      * @see SyncSource
162      *
163      * This implementation cycles through all SyncItems looking for the
164      * specified key.
165      */

166     public SyncItem getSyncItemFromId(Principal JavaDoc principal, SyncItemKey syncItemKey)
167     throws SyncSourceException {
168         SyncItem[] all = this.getAllSyncItems(principal);
169
170         for (int i=0; ((all != null) && (i<all.length)); ++i) {
171             if (syncItemKey.equals(all[i].getKey())) {
172                 return all[i];
173             }
174         }
175
176         return null;
177     }
178
179     /*
180      * @see SyncSource
181      *
182      */

183     public SyncItem[] getSyncItemsFromIds(Principal JavaDoc principal, SyncItemKey[] syncItemKeys)
184     throws SyncSourceException {
185         ArrayList JavaDoc ret = new ArrayList JavaDoc();
186         SyncItem syncItem = null;
187
188         for (int i=0; ((syncItemKeys != null) && (i<syncItemKeys.length)); ++i) {
189             syncItem = getSyncItemFromId(principal, syncItemKeys[i]);
190
191             if (syncItem != null) {
192                 ret.add(syncItem);
193             }
194         }
195
196         return (SyncItem[])ret.toArray(new SyncItem[ret.size()]);
197     }
198
199     /*
200     * @see SyncSource
201     */

202     public SyncItemKey[] getNewSyncItemKeys(Principal JavaDoc principal,
203                                             Timestamp JavaDoc since )
204     throws SyncSourceException {
205         return extractKeys(newSyncItems);
206     }
207
208     /*
209     * @see SyncSource
210     */

211     public SyncItem[] getNewSyncItems(Principal JavaDoc principal,
212                                       Timestamp JavaDoc since )
213     throws SyncSourceException {
214         return (SyncItem[])newSyncItems.toArray(new SyncItem[0]);
215     }
216
217     /*
218     * @see SyncSource
219     */

220     public SyncItemKey[] getDeletedSyncItemKeys(Principal JavaDoc principal,
221                                                 Timestamp JavaDoc since )
222     throws SyncSourceException {
223         return extractKeys(deletedSyncItems);
224     }
225
226     /*
227     * @see SyncSource
228     */

229     public SyncItem[] getDeletedSyncItems(Principal JavaDoc principal,
230                                           Timestamp JavaDoc since )
231     throws SyncSourceException {
232         return (SyncItem[])deletedSyncItems.toArray(new SyncItem[0]);
233     }
234
235     /**
236      * @return an array of keys containing the <i>SyncItem</i>'s key of the updated
237      * items after the last synchronizazion.
238      */

239     public SyncItemKey[] getUpdatedSyncItemKeys(Principal JavaDoc principal,
240                                                 Timestamp JavaDoc since )
241     throws SyncSourceException {
242         return extractKeys(updatedSyncItems);
243     }
244
245     /**
246     * @see SyncSource
247     */

248     public SyncItem[] getUpdatedSyncItems(Principal JavaDoc principal,
249                                           Timestamp JavaDoc since )
250     throws SyncSourceException {
251         return (SyncItem[])updatedSyncItems.toArray(new SyncItem[0]);
252     }
253
254     /*
255     * @see SyncSource
256     */

257     public void removeSyncItem(Principal JavaDoc principal, SyncItem syncItem)
258     throws SyncSourceException {
259         newSyncItems.remove (syncItem);
260         updatedSyncItems.remove (syncItem);
261
262         deletedSyncItems.add(syncItem);
263     }
264
265     /*
266     * @see SyncSource
267     */

268     public void removeSyncItems(Principal JavaDoc principal, SyncItem[] syncItems)
269     throws SyncSourceException {
270         for (int i=0; i<syncItems.length; ++i) {
271             removeSyncItem(principal, syncItems[i]);
272         } // next i
273
}
274
275     /**
276     * @see SyncSource
277     */

278     public SyncItem[] getAllSyncItems(Principal JavaDoc principal)
279     throws SyncSourceException {
280         return (SyncItem[])existingSyncItems.toArray(new SyncItemImpl[0]);
281     }
282
283     /**
284      * @see SyncSource
285      */

286     public boolean isModified(Principal JavaDoc principal, Timestamp JavaDoc since) {
287         try {
288             return (getDeletedSyncItems(principal, since).length > 0)
289                 || (getNewSyncItems(principal, since).length > 0)
290                 || (getUpdatedSyncItems(principal, since).length > 0);
291         } catch (Exception JavaDoc e) {
292             e.printStackTrace();
293             return false;
294         }
295     }
296
297     /**
298      * Initializes this MemorySyncSource with items.
299      *
300      * @param deletedItems the deleted items
301      * @param newItems the created items
302      * @param updatedItems the updated items
303      */

304     public void initialize(List JavaDoc deletedItems ,
305                            List JavaDoc newItems ,
306                            List JavaDoc updatedItems ) {
307
308        //
309
// Large object handling
310
// =====================
311
// If the server is still retrieving a large object, lo will be set to
312
// the item being retrieved and the new piece of data must be merged to
313
// the previous content. The partial item must be then removed by the
314
// list that contains it. When the LO is fully retrieved, the complete
315
// item will be updated in the containing list and from now on it will
316
// like any other item. The only difference is that the property
317
// PROPERTY_SIZE will be set to the expected content size
318
//
319
if (deletedSyncItems == null) {
320            deletedSyncItems = new ArrayList JavaDoc();
321        } else {
322            deletedSyncItems.clear();
323        }
324
325        if (newSyncItems == null) {
326            newSyncItems = new ArrayList JavaDoc();
327        } else {
328            newSyncItems.clear();
329        }
330
331        if (updatedSyncItems == null) {
332            updatedSyncItems = new ArrayList JavaDoc();
333        } else {
334            updatedSyncItems.clear();
335        }
336
337        deletedSyncItems.addAll(deletedItems) ;
338        newSyncItems.addAll(newItems) ;
339        updatedSyncItems.addAll(updatedItems ) ;
340
341        boolean cont = false;
342        do {
343            cont = handleLOs();
344        } while (cont);
345
346        //
347
// We have to make sure that the complete view is consistent so that
348
// new, modified or deleted items are not in the unchanged list; that
349
// deleted items are not in new, update and unchanged lists, and so on.
350
//
351
SyncItem syncItem = null;
352        Iterator JavaDoc i = deletedSyncItems.iterator();
353        while (i.hasNext()) {
354            syncItem = (SyncItem)i.next();
355            newSyncItems.remove(syncItem) ;
356            updatedSyncItems.remove(syncItem) ;
357        }
358
359        i = updatedSyncItems.iterator();
360        while (i.hasNext()) {
361            syncItem = (SyncItem)i.next();
362            newSyncItems.remove(syncItem) ;
363            deletedSyncItems.remove(syncItem);
364        }
365
366        i = newSyncItems.iterator();
367        while (i.hasNext()) {
368            syncItem = (SyncItem)i.next();
369            updatedSyncItems.remove(syncItem);
370            deletedSyncItems.remove(syncItem);
371        }
372     }
373
374
375     /**
376      * Sets the existing items
377      *
378      * @param items existing items
379      *
380      */

381     public void setExistingItems(List JavaDoc items) {
382         existingSyncItems = items;
383     }
384
385
386     /**
387      * Search the items for an item with exactly the same binary content.
388      *
389      * @return an item from a twin item. Each source implementation is free to
390      * interpret this as it likes (i.e.: comparing all fields).
391      *
392      * @param principal
393      * @param syncItemTwin the twin item
394      *
395      * @throws SyncSourceException in case of errors (for instance if the
396      * underlying data store runs into problems)
397      *
398      */

399     public SyncItem getSyncItemFromTwin(Principal JavaDoc principal, SyncItem syncItemTwin)
400     throws SyncSourceException {
401         SyncItem[] syncItems =
402             getSyncItemsFromTwins(principal, new SyncItem[] {syncItemTwin});
403
404         if ((syncItems == null) || (syncItems.length == 0)) {
405             return null; // not found
406
}
407
408         return syncItems[0];
409     }
410
411     /**
412      * Two items are considered twin if their binary content, converted to
413      * String are equal.
414      *
415      * @param principal
416      * @param syncItemTwins the twin items
417      *
418      * @return an array of items from corresponding twin items.
419      *
420      * @throws SyncSourceException in case of errors (for instance if the
421      * underlying data store runs into problems)
422      *
423      */

424     public SyncItem[] getSyncItemsFromTwins(Principal JavaDoc principal, SyncItem[] syncItemTwins)
425     throws SyncSourceException {
426         ArrayList JavaDoc ret = new ArrayList JavaDoc();
427
428         String JavaDoc[] contents = new String JavaDoc[syncItemTwins.length];
429
430         for (int i=0; i<syncItemTwins.length; ++i) {
431             contents[i] = new String JavaDoc((byte[])syncItemTwins[i].getProperty(SyncItem.PROPERTY_BINARY_CONTENT).getValue());
432         }
433
434         SyncItem[] all = getAllSyncItems(principal);
435
436         String JavaDoc content = null;
437         SyncProperty prop = null;
438         for (int i=0; i<all.length; ++i) {
439             prop = all[i].getProperty(SyncItem.PROPERTY_BINARY_CONTENT);
440             if (prop != null) {
441                 content = new String JavaDoc((byte[])prop.getValue());
442             }
443
444             for (int j=0; ((content != null) && (j<contents.length)); ++j) {
445                 if (content.equals(contents[j])) {
446                     ret.add(all[i]);
447                     break;
448                 }
449             }
450         }
451
452         return (SyncItem[])ret.toArray(new SyncItem[ret.size()]);
453     }
454
455     // --------------------------------------------------------- Private methods
456

457     private SyncItemKey[] extractKeys(Collection JavaDoc syncItems) {
458         SyncItemKey[] keys = new SyncItemKey[syncItems.size()];
459
460         SyncItem syncItem = null;
461         int j = 0;
462         for(Iterator JavaDoc i = syncItems.iterator(); i.hasNext(); ++j) {
463            syncItem = (SyncItem)i.next();
464
465            keys[j] = syncItem.getKey();
466         } // next i, j
467

468         return keys;
469     }
470
471     private SyncItemKey[] extractKeys(SyncItem[] syncItems) {
472         SyncItemKey[] keys = new SyncItemKey[syncItems.length];
473
474         for(int j = 0; j<syncItems.length; ++j) {
475            keys[j] = syncItems[j].getKey();
476         } // next i, j
477

478         return keys;
479     }
480
481     /**
482      * Look for LO and handle it accordingly.
483      *
484      * @return true if there could be something still to process, false otherwise
485      */

486     private boolean handleLOs() {
487         boolean ret = false;
488         
489         SyncItem chunk = null;
490         int p = -1;
491
492         if (lo != null) {
493             // We are reading a LO
494
p = newSyncItems.indexOf(lo);
495             if (p >= 0) {
496                 chunk = (SyncItem)newSyncItems.get(p);
497             } else {
498                 p = updatedSyncItems.indexOf(lo);
499                 if (p >= 0) {
500                     chunk = (SyncItem)updatedSyncItems.get(p);
501                 }
502             }
503
504             if (chunk != null) {
505                 mergeLOChunk(chunk);
506
507                 //
508
// if the chunk is still in the state 'P', it means that the
509
// current LO is not complete. Therefore, we need to not add
510
// the item to the news or updated lists.
511
//
512
if (chunk.getState() != SyncItemState.PARTIAL) {
513                 switch (lo.getState()) {
514                     case SyncItemState.PARTIAL:
515                         //
516
// just a new chunk
517
//
518
break;
519
520                     case SyncItemState.NEW:
521                         //
522
// Last chunk of an addition
523
//
524
newSyncItems.remove(chunk);
525                         newSyncItems.add(lo);
526                             lo = null; ret = true;
527                         break;
528
529                     case SyncItemState.UPDATED:
530                     case SyncItemState.SYNCHRONIZED:
531                         //
532
// Last chunk of an update
533
//
534
updatedSyncItems.remove(chunk);
535                         updatedSyncItems.add(lo);
536                             lo = null; ret = true;
537                         break;
538                 }
539                 } // chunk state
540
} // chunk != null
541
} else {
542             //
543
// Look for the first chunk
544
//
545

546             Iterator JavaDoc i = newSyncItems.iterator();
547             while(i.hasNext()) {
548                 chunk = (SyncItem)i.next();
549
550                 if (chunk.getState() == SyncItemState.PARTIAL) {
551                     lo = newSyncItem(chunk);
552                     lo.setState(SyncItemState.NEW);
553                     break;
554                 }
555             }
556
557             i = updatedSyncItems.iterator();
558             while(i.hasNext()) {
559                 chunk = (SyncItem)i.next();
560
561                 if (chunk.getState() == SyncItemState.PARTIAL) {
562                     lo = newSyncItem(chunk);
563                     lo.setState(SyncItemState.UPDATED);
564                     break;
565                 }
566             }
567         }
568         
569         return ret;
570     }
571
572     /**
573      * Merges the given <code>chunk</code> with the existing <code>lo</code>
574      * item.
575      *
576      * @param cunk the chunk to merge
577      *
578      */

579     private void mergeLOChunk(SyncItem chunk) {
580         if (lo == null) {
581             //
582
// we should never get here!
583
//
584
return;
585         }
586
587         byte[] loData = (byte[])lo.getPropertyValue(lo.PROPERTY_BINARY_CONTENT);
588         byte[] chunkData = (byte[])chunk.getPropertyValue(lo.PROPERTY_BINARY_CONTENT);
589
590         byte[] allData = new byte[loData.length + chunkData.length];
591
592         System.arraycopy(loData, 0, allData, 0, loData.length);
593         System.arraycopy(chunkData, 0, allData, loData.length, chunkData.length);
594
595         SyncProperty size = lo.getProperty(lo.PROPERTY_SIZE);
596         lo.setProperties(chunk.getProperties());
597         lo.setPropertyValue(lo.PROPERTY_BINARY_CONTENT, allData);
598         lo.setProperty(size);
599     }
600
601     /**
602      * Create a new <i>SyncItem</i> with the content of an existing one.
603      *
604      * @param key
605      * @param fromSyncItem
606      */

607     private SyncItem newSyncItem(SyncItem fromSyncItem) {
608         SyncItemKey mappedKey = fromSyncItem.getMappedKey();
609
610         String JavaDoc mappedKeyValue = (mappedKey == null)
611                               ? null
612                               : mappedKey.getKeyAsString()
613                               ;
614         SyncItemImpl syncItem
615             = new SyncItemImpl(fromSyncItem.getSyncSource() ,
616                                fromSyncItem.getKey().getKeyAsString(),
617                                mappedKeyValue ,
618                                fromSyncItem.getState() );
619
620         syncItem.setProperties(fromSyncItem.getProperties());
621
622         return syncItem;
623     }
624
625     public void commitSync() throws SyncSourceException {
626         allNewSyncItems.addAll(newSyncItems);
627         allUpdatedSyncItems.addAll(updatedSyncItems);
628         allDeletedSyncItems.addAll(deletedSyncItems);
629     }
630
631     public SyncItem[] getAllNewSyncItems(Principal JavaDoc principal, Timestamp JavaDoc since)
632     throws SyncSourceException {
633         return (SyncItem[])allNewSyncItems.toArray(new SyncItem[0]);
634     }
635
636     public SyncItem[] getAllUpdatedSyncItems(Principal JavaDoc principal,Timestamp JavaDoc since)
637     throws SyncSourceException {
638         return (SyncItem[])allUpdatedSyncItems.toArray(new SyncItem[0]);
639     }
640
641     public SyncItem[] getAllDeletedSyncItems(Principal JavaDoc principal, Timestamp JavaDoc since)
642     throws SyncSourceException {
643         return (SyncItem[])allDeletedSyncItems.toArray(new SyncItem[0]);
644     }
645
646 }
Popular Tags