KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > syncclient > test > SIFSyncSource


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
19 package sync4j.syncclient.test;
20
21 import java.io.*;
22 import java.security.Principal JavaDoc;
23
24 import java.util.Date JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.Vector JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Properties JavaDoc;
29
30 import sync4j.syncclient.common.SourceUtils;
31
32 import sync4j.syncclient.spds.engine.*;
33 import sync4j.syncclient.spds.SyncException;
34
35 import sync4j.framework.tools.Base64;
36 import sync4j.framework.tools.IOTools;
37
38
39 /**
40  * This class implements a dummy <i>SyncSource</i> that just displays the calls
41  * to its methods
42  *
43  * @author Stefano Fornari
44  *
45  * @version $Id: SIFSyncSource.java,v 1.9 2005/04/13 13:39:40 harrie Exp $
46  *
47  */

48 public class SIFSyncSource implements SyncSource {
49
50     // --------------------------------------------------------------- Constants
51

52     public static final String JavaDoc DATABASE_FILE_NAME
53         = "sync.db" ;
54     public static final String JavaDoc DATABASE_HEADER
55         = "FileSystemSyncSource file database" ;
56     public static final String JavaDoc LOG_NAME
57         = "sync4j.framework.engine" ;
58
59     // -------------------------------------------------------------- Properties
60

61     /**
62      * The directory where files are stored (the default is the current
63      * directory) - read/write
64      */

65     private String JavaDoc sourceDirectory = ".";
66
67     public void setSourceDirectory(String JavaDoc sourceDirectory) {
68         this.sourceDirectory = sourceDirectory;
69     }
70
71     public String JavaDoc getSourceDirectory() {
72         return this.sourceDirectory;
73     }
74
75     private String JavaDoc name;
76
77     public String JavaDoc getName() {
78         return name;
79     }
80
81     public void setName(String JavaDoc name) {
82         this.name = name;
83     }
84
85     private String JavaDoc type;
86
87     public String JavaDoc getType() {
88         return this.type;
89     }
90
91     public void setType(String JavaDoc type) {
92         this.type = type;
93     }
94
95     private boolean encode = false;
96
97     public boolean isEncode() {
98         return encode;
99     }
100
101     public void setEncode(boolean encode) {
102         this.encode = encode;
103     }
104
105     public void setEncode(String JavaDoc encode) {
106
107         if ("true".equals(encode)) {
108             this.encode = true ;
109         } else {
110             this.encode = false ;
111         }
112     }
113
114     private String JavaDoc sourceURI;
115
116     /** Getter for property uri.
117      * @return Value of property uri.
118      */

119     public String JavaDoc getSourceURI() {
120         return sourceURI;
121     }
122
123     /** Setter for property uri.
124      * @param sourceURI New value of property uri.
125      */

126     public void setSourceURI(String JavaDoc sourceURI) {
127         this.sourceURI = sourceURI;
128     }
129
130     // ------------------------------------------------------------ Constructors
131

132     /** Creates a new instance of AbstractSyncSource */
133     public SIFSyncSource() {
134     }
135
136     // ---------------------------------------------------------- Public methods
137

138     public void beginSync(int type) throws SyncException {
139         restoreSyncDB ();
140     }
141
142
143     public void endSync() throws SyncException {
144     }
145
146
147     public SyncItem[] getAllSyncItems(Principal JavaDoc principal) throws SyncException {
148         return filterSyncItems(principal, null, SyncItemState.UNKNOWN);
149     }
150
151
152     public SyncItem[] getDeletedSyncItems(Principal JavaDoc principal,
153                                           Date JavaDoc since ) throws SyncException {
154         return filterSyncItems(principal, since, SyncItemState.DELETED);
155
156     }
157
158
159     public SyncItem[] getNewSyncItems(Principal JavaDoc principal,
160                                       Date JavaDoc since ) throws SyncException {
161         return filterSyncItems(principal, since, SyncItemState.NEW);
162
163     }
164
165     public SyncItem[] getUpdatedSyncItems(Principal JavaDoc principal,
166                                           Date JavaDoc since ) throws SyncException {
167         return filterSyncItems(principal, since, SyncItemState.UPDATED);
168
169     }
170
171     public void removeSyncItem(Principal JavaDoc principal, SyncItem syncItem) throws SyncException {
172         String JavaDoc fileName = syncItem.getKey().getKeyAsString();
173
174         new File(sourceDirectory, fileName).delete();
175
176         removeState(principal, fileName);
177     }
178
179     /*
180     * @see SyncSource
181     */

182
183     public SyncItem setSyncItem(Principal JavaDoc principal, SyncItem syncItem)
184     throws SyncException {
185
186         try {
187             String JavaDoc fileName = syncItem.getKey().getKeyAsString();
188             byte[] fileContent =
189                 (byte[])syncItem.getPropertyValue(SyncItem.PROPERTY_BINARY_CONTENT);
190
191             if (fileContent == null) {
192                 fileContent = new byte[0];
193             }
194
195             HashMap JavaDoc hashMap = null;
196             HashMap JavaDoc hashMapFromFile = null;
197
198             File f = new File (sourceDirectory, fileName);
199
200             if (encode && fileContent.length > 0) {
201
202
203                 if (f.exists()) {
204                     hashMapFromFile = SourceUtils.
205                         xmlToHashMap(IOTools.readFileString(f));
206
207                     hashMap = SourceUtils.xmlToHashMap
208                         (new String JavaDoc(Base64.decode(fileContent)));
209
210                     hashMapFromFile.putAll(hashMap);
211
212                     IOTools.writeFile(
213                         SourceUtils.hashMapToXml(hashMapFromFile), f);
214
215
216                 }
217                 else {
218
219                     hashMapFromFile = new HashMap JavaDoc();
220
221                     hashMap = SourceUtils.xmlToHashMap
222                         (new String JavaDoc(Base64.decode(fileContent)));
223
224                     hashMapFromFile.putAll(hashMap);
225
226                     IOTools.writeFile(
227                         SourceUtils.hashMapToXml(hashMapFromFile), f);
228                 }
229
230             } else {
231
232                 if (f.exists()) {
233
234                     hashMapFromFile = SourceUtils.
235                         xmlToHashMap(IOTools.readFileString(f));
236
237                     hashMap = SourceUtils.
238                         xmlToHashMap(new String JavaDoc(fileContent));
239
240                     hashMapFromFile.putAll(hashMap);
241
242                     IOTools.writeFile(
243                         SourceUtils.hashMapToXml(hashMapFromFile), f);
244
245                 } else {
246
247                     hashMapFromFile = new HashMap JavaDoc();
248
249                     hashMap = SourceUtils.
250                         xmlToHashMap(new String JavaDoc(fileContent));
251
252                     hashMapFromFile.putAll(hashMap);
253
254                     IOTools.writeFile(
255                         SourceUtils.hashMapToXml(hashMapFromFile), f);
256
257                 }
258
259             }
260
261             Date JavaDoc t = (Date JavaDoc) syncItem.getPropertyValue(SyncItem.PROPERTY_TIMESTAMP);
262
263             f.setLastModified(t.getTime());
264
265             setState(principal, fileName, SyncItemState.SYNCHRONIZED);
266
267             SyncItem newSyncItem =
268                 new SyncItemImpl(this, fileName, SyncItemState.NEW);
269
270             newSyncItem.setProperties(syncItem.getProperties());
271
272             return newSyncItem;
273         } catch (IOException e) {
274             throw new SyncException( "Error setting the item "
275                                       + syncItem
276                                       , e
277                                       );
278         }
279         catch (Exception JavaDoc e) {
280             throw new SyncException( "Error setting the hashmap in item "
281                                      + syncItem
282                                      , e
283                                      );
284         }
285     }
286
287     public void commitSync() {
288     }
289
290     // ------------------------------------------------------------ Private data
291
/**
292      * The synchronization database is stored in a file whose name is given
293      * by the value of the constant DATABASE_FILE_NAME prefixed by the user name
294      * and the device id. For example:
295      * <pre>
296      * guest.Sync4jTest.sync.db
297      * </pre>
298      * The database is a property file where each entry has the following
299      * format:
300      * <pre>
301      * [filename]=[state][lastmodified_timestamp]
302      * </pre>
303      * For example:
304      * <blockquote>
305      * readme.txt=U98928743098094
306      * </blockquote>
307      * <p>
308      * updateSyncDatabase works as follows:
309      * <pre>
310      * 1. Read the existing database (if it exists)
311      * 2. Scan the source directory getting all files in the directory
312      * 3. For each file f in the source directory
313      * 3.1. If f is already in the database
314      * 3.1.1. If f has been modified after the lastmodified_timestamp stored into the database
315      * 3.1.1.1. Set the state of the file to UPDATE and store the new lastmodified_timestamp
316      * 3.2. Else
317      * 3.2.1. Add f to the database setting its state to NEW and store the lastmodified_timestamp
318      * 3. End For each
319      * 4. For each file f in the database
320      * 4.1. If f does not exist in the source directory
321      * 4.1.1. Set the state to DELETED
322      * 5. End For each
323      * </pre>
324      * <p>
325      * At the end of the process, the updated database is saved and than returned.
326      *
327      * @return the updated databse. In case of error, an error message is traced
328      * and an empty Properties object is returned.
329      */

330     private Properties JavaDoc updateSyncDatabase(Principal JavaDoc principal) {
331
332         Properties JavaDoc syncDatabase = new Properties JavaDoc();
333
334         try {
335             File fileSyncDatabase = getDatabaseFile(principal);
336
337             //
338
// Reads the existing database
339
//
340
if (fileSyncDatabase.exists()) {
341                 FileInputStream fis = new FileInputStream(fileSyncDatabase);
342                 syncDatabase.load(fis);
343                 fis.close();
344             }
345
346             //
347
// Get the list of files in the source directory
348
//
349
Vector JavaDoc existingFiles = getExistingFiles();
350
351             //
352
// Get the list of the file in the databsae
353
//
354
Enumeration JavaDoc databaseFiles = syncDatabase.propertyNames();
355
356             String JavaDoc state = null,
357                    fileName = null;
358
359             long lastModified;
360
361             int n = existingFiles.size();
362             for (int i=0; i < n; ++i) {
363                 fileName = (String JavaDoc)existingFiles.elementAt(i);
364                 lastModified = new File(sourceDirectory, fileName).lastModified();
365
366                 state = syncDatabase.getProperty(fileName);
367
368                 if (state != null) {
369                     //
370
// The file is already in the database
371
//
372
if (lastModified > lastModifiedFromStateString(state)) {
373                         state = buildStateString(SyncItemState.UPDATED, lastModified);
374                         syncDatabase.put(fileName, state);
375                     }
376                 } else {
377                     //
378
// The file is not in the database
379
//
380
state = buildStateString(SyncItemState.NEW, lastModified);
381                     syncDatabase.put(fileName, state);
382                 }
383             } // next i
384

385             for (; databaseFiles.hasMoreElements();) {
386                 fileName = (String JavaDoc)databaseFiles.nextElement();
387
388                 if (!existingFiles.contains(fileName)) {
389                     state = buildStateString(SyncItemState.DELETED, System.currentTimeMillis());
390                     syncDatabase.put(fileName, state);
391                 }
392             } // next
393

394             //
395
// Save & return
396
//
397
FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
398             syncDatabase.save(fos, DATABASE_HEADER);
399             fos.close();
400         } catch (IOException e) {
401             e.printStackTrace();
402         }
403
404         return syncDatabase;
405     }
406
407     private File getDatabaseFile(Principal JavaDoc principal) {
408         return new File(sourceDirectory + '.' + DATABASE_FILE_NAME);
409     }
410
411     private String JavaDoc buildStateString(char state, long lastModified) {
412         return state + String.valueOf(lastModified);
413     }
414
415     private long lastModifiedFromStateString(String JavaDoc state) {
416         return Long.parseLong(state.substring(1));
417     }
418
419     private char stateFromStateString(String JavaDoc state) {
420         if ((state == null) || (state.length() == 0)) return SyncItemState.UNKNOWN;
421
422         return state.charAt(0);
423     }
424
425     private Vector JavaDoc getExistingFiles() throws IOException {
426         Vector JavaDoc ret = new Vector JavaDoc();
427
428         String JavaDoc[] files = new File(sourceDirectory).list();
429
430         if (files != null) {
431             for (int i = 0; i<files.length; ++i) {
432                 if (!files[i].endsWith('.' + DATABASE_FILE_NAME)) {
433                     ret.addElement(files[i]);
434                 }
435             } // next i
436
}
437
438         return ret;
439     }
440
441     private void setState(Principal JavaDoc principal, String JavaDoc file, char state) {
442         try {
443             Properties JavaDoc syncDatabase = new Properties JavaDoc();
444
445             File fileSyncDatabase = getDatabaseFile(principal);
446
447             //
448
// Reads the existing database
449
//
450
if (fileSyncDatabase.exists()) {
451                 FileInputStream fis = new FileInputStream(fileSyncDatabase);
452                 syncDatabase.load(fis);
453                 fis.close();
454             }
455
456             syncDatabase.put(file, buildStateString(state, System.currentTimeMillis()));
457
458             FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
459             syncDatabase.save(fos, DATABASE_HEADER);
460             fos.close();
461         } catch (IOException e) {
462             e.printStackTrace();
463         }
464     }
465
466     private void removeState(Principal JavaDoc principal, String JavaDoc file) {
467         try {
468             File fileSyncDatabase = getDatabaseFile(principal);
469
470             if (!fileSyncDatabase.exists()) return;
471
472             Properties JavaDoc syncDatabase = new Properties JavaDoc();
473
474             //
475
// Reads the existing database
476
//
477

478             FileInputStream fis = new FileInputStream(fileSyncDatabase);
479             syncDatabase.load(fis);
480             fis.close();
481
482             syncDatabase.remove(file);
483
484             FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
485             syncDatabase.save(fos, DATABASE_HEADER);
486             fos.close();
487         } catch (IOException e) {
488             e.printStackTrace();
489         }
490     }
491
492     /**
493      * Filters the SyncItems in the synchronization database (after a refresh)
494      * based on the given principal, last sync timestamp and state (see
495      * SyncItemState). If state is equals to UNKNOWN all items are returned.<br>
496      * Note that the current implementation ignores the principal: data do not
497      * depend on users.
498      *
499      * @param principal principal. null means any
500      * @param since last sync timestamp. null neans since ever
501      * @param state the state to use as filter
502      *
503      * @return an array of SyncItem objects whose state is equal to the given
504      * state.
505      */

506     private SyncItem[] filterSyncItems(Principal JavaDoc principal,
507                                        Date JavaDoc since ,
508                                        char state ) {
509         Properties JavaDoc syncDatabase = updateSyncDatabase(principal);
510
511         Vector JavaDoc syncItems = new Vector JavaDoc();
512
513         long fileTimestamp,
514              sinceTimestamp = (since == null) ? -1 : since.getTime();
515
516         SyncItem syncItem = null;
517         String JavaDoc fileName = null;
518         String JavaDoc stateString = null;
519         char fileState ;
520         for (Enumeration JavaDoc e = syncDatabase.keys(); e.hasMoreElements(); ) {
521             fileName = (String JavaDoc)e.nextElement();
522             stateString = (String JavaDoc)syncDatabase.get(fileName);
523             fileState = stateFromStateString(stateString);
524             if ((state == SyncItemState.UNKNOWN) || (fileState == state)) {
525                 fileTimestamp = lastModifiedFromStateString(stateString);
526                 if (fileTimestamp > sinceTimestamp ) {
527                     syncItem = new SyncItemImpl(this, fileName, fileState);
528                     if (encode){
529                         syncItem.setProperty(
530                             new SyncItemProperty(SyncItem.PROPERTY_BINARY_CONTENT ,
531                                                  Base64.encode(readFileContent(fileName)))
532                         );
533                     } else {
534                         syncItem.setProperty(
535                             new SyncItemProperty(SyncItem.PROPERTY_BINARY_CONTENT,
536                                                  readFileContent(fileName) )
537                         );
538                     }
539                     syncItems.addElement(syncItem);
540                 }
541             }
542         } // next e
543

544         SyncItem[] ret = new SyncItem[syncItems.size()];
545         for (int i=0; i<ret.length; ++i) {
546             ret[i] = (SyncItem)syncItems.elementAt(i);
547         }
548
549         return ret;
550     }
551
552     /**
553      * Reads the content of the given file.
554      *
555      * @param fileName the name of the file to read
556      *
557      * @return the file content as a byte[]
558      */

559     private byte[] readFileContent(String JavaDoc fileName) {
560         byte buf[] = null;
561
562         try {
563             File file = new File(sourceDirectory, fileName);
564
565             if (!file.exists()) return new byte[0];
566
567             buf = new byte[(int)file.length()];
568
569             FileInputStream fis = new FileInputStream(file);
570             fis.read(buf);
571             fis.close();
572         } catch (IOException e) {
573             buf = new byte[0];
574             e.printStackTrace();
575         }
576
577         return buf;
578     }
579
580     /**
581      * remove old deleted files from syncDB
582      */

583     private void restoreSyncDB () {
584
585         Properties JavaDoc syncDatabase = new Properties JavaDoc();
586
587         String JavaDoc state = null;
588         String JavaDoc fileName = null;
589
590         try {
591
592             File fileSyncDatabase = getDatabaseFile(null);
593
594             //
595
// Reads the existing database
596
//
597
if (fileSyncDatabase.exists()) {
598                 FileInputStream fis = new FileInputStream(fileSyncDatabase);
599                 syncDatabase.load(fis);
600                 fis.close();
601             }
602
603             //
604
// Get the list of the file in the databsae
605
//
606
Enumeration JavaDoc databaseFiles = syncDatabase.propertyNames();
607
608             for (; databaseFiles.hasMoreElements();) {
609                 fileName = (String JavaDoc)databaseFiles.nextElement();
610
611                 state = syncDatabase.getProperty(fileName);
612
613                 if (stateFromStateString(state) == SyncItemState.DELETED) {
614                     syncDatabase.remove(fileName);
615                 }
616             } // next
617

618
619             //
620
// Save & return
621
//
622
FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
623             syncDatabase.save(fos, DATABASE_HEADER);
624             fos.close();
625         } catch (IOException e) {
626             e.printStackTrace();
627         }
628
629     }
630
631
632 }
633
Popular Tags