KickJava   Java API By Example, From Geeks To Geeks.

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


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 import java.util.Date JavaDoc;
24 import java.util.Vector JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.Properties JavaDoc;
27
28 import sync4j.syncclient.spds.engine.*;
29 import sync4j.syncclient.spds.SyncException;
30
31 import sync4j.framework.tools.Base64;
32
33
34 /**
35  * This class implements a file system <i>SyncSource</i> that just displays
36  * the calls to its methods
37  *
38  * @author Stefano Fornari
39  *
40  * @version $Id: FileSystemSyncSource.java,v 1.6 2005/05/09 12:46:57 luigiafassina Exp $
41  *
42  */

43 public class FileSystemSyncSource implements SyncSource {
44
45     // --------------------------------------------------------------- Constants
46

47     public static final String JavaDoc DATABASE_FILE_NAME
48         = "sync.db" ;
49     public static final String JavaDoc DATABASE_HEADER
50         = "FileSystemSyncSource file database" ;
51     public static final String JavaDoc LOG_NAME
52         = "sync4j.framework.engine" ;
53
54     // -------------------------------------------------------------- Properties
55

56     /**
57      * The directory where files are stored (the default is the current
58      * directory) - read/write
59      */

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

106     public String JavaDoc getSourceURI() {
107         return sourceURI;
108     }
109
110     /** Setter for property uri.
111      * @param sourceURI New value of property uri.
112      */

113     public void setSourceURI(String JavaDoc sourceURI) {
114         this.sourceURI = sourceURI;
115     }
116
117     // ------------------------------------------------------------ Constructors
118

119     /** Creates a new instance of AbstractSyncSource */
120     public FileSystemSyncSource() {
121     }
122
123     // ---------------------------------------------------------- Public methods
124
public void beginSync(int type) throws SyncException {
125
126         //
127
// Check if sourceDir is existing: if doesn't exist the syncsource
128
// have to be synchronized
129
//
130
File f = new File(sourceDirectory);
131         if (!f.isDirectory()) {
132             throw new SyncException(
133                 "Destination directory (" + sourceDirectory + ") not existing."
134             );
135         }
136         
137         restoreSyncDB();
138     }
139
140     public void endSync() throws SyncException {
141     }
142
143     public SyncItem[] getAllSyncItems(Principal JavaDoc principal) throws SyncException {
144         return filterSyncItems(principal, null, SyncItemState.UNKNOWN);
145     }
146
147     public SyncItem[] getDeletedSyncItems(Principal JavaDoc principal,
148                                           Date JavaDoc since ) throws SyncException {
149         return filterSyncItems(principal, since, SyncItemState.DELETED);
150     }
151
152     public SyncItem[] getNewSyncItems(Principal JavaDoc principal,
153                                       Date JavaDoc since ) throws SyncException {
154         return filterSyncItems(principal, since, SyncItemState.NEW);
155     }
156
157     public SyncItem[] getUpdatedSyncItems(Principal JavaDoc principal,
158                                           Date JavaDoc since ) throws SyncException {
159         return filterSyncItems(principal, since, SyncItemState.UPDATED);
160     }
161
162     public void removeSyncItem(Principal JavaDoc principal, SyncItem syncItem) throws SyncException {
163         String JavaDoc fileName = syncItem.getKey().getKeyAsString();
164
165         new File(sourceDirectory, fileName).delete();
166
167         removeState(principal, fileName);
168     }
169
170     public SyncItem setSyncItem(Principal JavaDoc principal, SyncItem syncItem)
171     throws SyncException {
172         try {
173             String JavaDoc fileName = syncItem.getKey().getKeyAsString();
174             byte[] fileContent =
175                 (byte[])syncItem.getPropertyValue(SyncItem.PROPERTY_BINARY_CONTENT);
176
177             FileOutputStream fos = new FileOutputStream(new File(sourceDirectory, fileName));
178             if (fileContent != null) {
179                if (encode) {
180                    fos.write(Base64.decode(fileContent));
181                } else {
182                    fos.write(fileContent);
183                }
184             }
185             fos.close();
186
187             setState(principal, fileName, SyncItemState.SYNCHRONIZED);
188
189             SyncItem newSyncItem =
190                 new SyncItemImpl(this, fileName, SyncItemState.NEW);
191
192             newSyncItem.setProperties(syncItem.getProperties());
193
194             return newSyncItem;
195         } catch (IOException e) {
196             throw new SyncException( "Error setting the item "
197                                    + syncItem
198                                    , e
199                                    );
200         }
201     }
202
203     public void commitSync() {
204     }
205
206     // ------------------------------------------------------------ Private data
207
/**
208      * The synchronization database is stored in a file whose name is given
209      * by the value of the constant DATABASE_FILE_NAME prefixed by the user name
210      * and the device id. For example:
211      * <pre>
212      * guest.Sync4jTest.sync.db
213      * </pre>
214      * The database is a property file where each entry has the following
215      * format:
216      * <pre>
217      * [filename]=[state][lastmodified_timestamp]
218      * </pre>
219      * For example:
220      * <blockquote>
221      * readme.txt=U98928743098094
222      * </blockquote>
223      * <p>
224      * updateSyncDatabase works as follows:
225      * <pre>
226      * 1. Read the existing database (if it exists)
227      * 2. Scan the source directory getting all files in the directory
228      * 3. For each file f in the source directory
229      * 3.1. If f is already in the database
230      * 3.1.1. If f has been modified after the lastmodified_timestamp stored into the database
231      * 3.1.1.1. Set the state of the file to UPDATE and store the new lastmodified_timestamp
232      * 3.2. Else
233      * 3.2.1. Add f to the database setting its state to NEW and store the lastmodified_timestamp
234      * 3. End For each
235      * 4. For each file f in the database
236      * 4.1. If f does not exist in the source directory
237      * 4.1.1. Set the state to DELETED
238      * 5. End For each
239      * </pre>
240      * <p>
241      * At the end of the process, the updated database is saved and than returned.
242      *
243      * @return the updated databse. In case of error, an error message is traced
244      * and an empty Properties object is returned.
245      */

246     protected Properties JavaDoc updateSyncDatabase(Principal JavaDoc principal) {
247
248         Properties JavaDoc syncDatabase = new Properties JavaDoc();
249
250         try {
251             File fileSyncDatabase = getDatabaseFile(principal);
252
253             //
254
// Reads the existing database
255
//
256
if (fileSyncDatabase.exists()) {
257                 FileInputStream fis = new FileInputStream(fileSyncDatabase);
258                 syncDatabase.load(fis);
259                 fis.close();
260             }
261
262             //
263
// Get the list of files in the source directory
264
//
265
Vector JavaDoc existingFiles = getExistingFiles();
266
267             //
268
// Get the list of the file in the databsae
269
//
270
Enumeration JavaDoc databaseFiles = syncDatabase.propertyNames();
271
272             String JavaDoc state = null,
273                    fileName = null;
274
275             long lastModified;
276
277             int n = existingFiles.size();
278             for (int i=0; i < n; ++i) {
279                 fileName = (String JavaDoc)existingFiles.elementAt(i);
280                 lastModified = new File(sourceDirectory, fileName).lastModified();
281
282                 state = syncDatabase.getProperty(fileName);
283
284                 if (state != null) {
285                     //
286
// The file is already in the database
287
//
288
if (lastModified > lastModifiedFromStateString(state)) {
289                         state = buildStateString(SyncItemState.UPDATED, lastModified);
290                         syncDatabase.put(fileName, state);
291                     }
292                 } else {
293                     //
294
// The file is not in the database
295
//
296
state = buildStateString(SyncItemState.NEW, lastModified);
297                     syncDatabase.put(fileName, state);
298                 }
299             } // next i
300

301             for (; databaseFiles.hasMoreElements();) {
302                 fileName = (String JavaDoc)databaseFiles.nextElement();
303
304                 if (!existingFiles.contains(fileName)) {
305                     state = buildStateString(SyncItemState.DELETED, System.currentTimeMillis());
306                     syncDatabase.put(fileName, state);
307                 }
308             } // next
309

310             //
311
// Save & return
312
//
313
FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
314             syncDatabase.save(fos, DATABASE_HEADER);
315             fos.close();
316         } catch (IOException e) {
317             e.printStackTrace();
318         }
319
320         return syncDatabase;
321     }
322
323     private File getDatabaseFile(Principal JavaDoc principal) {
324         return new File(sourceDirectory + '.' + DATABASE_FILE_NAME);
325     }
326
327     private String JavaDoc buildStateString(char state, long lastModified) {
328         return state + String.valueOf(lastModified);
329     }
330
331     protected long lastModifiedFromStateString(String JavaDoc state) {
332         return Long.parseLong(state.substring(1));
333     }
334
335     protected char stateFromStateString(String JavaDoc state) {
336         if ((state == null) || (state.length() == 0)) return SyncItemState.UNKNOWN;
337
338         return state.charAt(0);
339     }
340
341     private Vector JavaDoc getExistingFiles() throws IOException {
342         Vector JavaDoc ret = new Vector JavaDoc();
343
344         String JavaDoc[] files = new File(sourceDirectory).list();
345
346         if (files != null) {
347             for (int i = 0; i<files.length; ++i) {
348                 if (!files[i].endsWith('.' + DATABASE_FILE_NAME)) {
349                     ret.addElement(files[i]);
350                 }
351             } // next i
352
}
353
354         return ret;
355     }
356
357     protected void setState(Principal JavaDoc principal, String JavaDoc file, char state) {
358         try {
359             Properties JavaDoc syncDatabase = new Properties JavaDoc();
360
361             File fileSyncDatabase = getDatabaseFile(principal);
362
363             //
364
// Reads the existing database
365
//
366
if (fileSyncDatabase.exists()) {
367                 FileInputStream fis = new FileInputStream(fileSyncDatabase);
368                 syncDatabase.load(fis);
369                 fis.close();
370             }
371
372             syncDatabase.put(file, buildStateString(state, System.currentTimeMillis()));
373
374             FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
375             syncDatabase.save(fos, DATABASE_HEADER);
376             fos.close();
377         } catch (IOException e) {
378             e.printStackTrace();
379         }
380     }
381
382     private void removeState(Principal JavaDoc principal, String JavaDoc file) {
383         try {
384             File fileSyncDatabase = getDatabaseFile(principal);
385
386             if (!fileSyncDatabase.exists()) return;
387
388             Properties JavaDoc syncDatabase = new Properties JavaDoc();
389
390             //
391
// Reads the existing database
392
//
393

394             FileInputStream fis = new FileInputStream(fileSyncDatabase);
395             syncDatabase.load(fis);
396             fis.close();
397
398             syncDatabase.remove(file);
399
400             FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
401             syncDatabase.save(fos, DATABASE_HEADER);
402             fos.close();
403         } catch (IOException e) {
404             e.printStackTrace();
405         }
406     }
407
408     /**
409      * Filters the SyncItems in the synchronization database (after a refresh)
410      * based on the given principal, last sync timestamp and state (see
411      * SyncItemState). If state is equals to UNKNOWN all items are returned.<br>
412      * Note that the current implementation ignores the principal: data do not
413      * depend on users.
414      *
415      * @param principal principal. null means any
416      * @param since last sync timestamp. null neans since ever
417      * @param state the state to use as filter
418      *
419      * @return an array of SyncItem objects whose state is equal to the given
420      * state.
421      */

422     protected SyncItem[] filterSyncItems(Principal JavaDoc principal,
423                                          Date JavaDoc since ,
424                                          char state )
425     throws SyncException {
426         Properties JavaDoc syncDatabase = updateSyncDatabase(principal);
427
428         Vector JavaDoc syncItems = new Vector JavaDoc();
429
430         long fileTimestamp,
431              sinceTimestamp = (since == null) ? -1 : since.getTime();
432
433         SyncItem syncItem = null;
434         String JavaDoc fileName = null;
435         String JavaDoc stateString = null;
436         char fileState ;
437         for (Enumeration JavaDoc e = syncDatabase.keys(); e.hasMoreElements(); ) {
438             fileName = (String JavaDoc)e.nextElement();
439             stateString = (String JavaDoc)syncDatabase.get(fileName);
440             fileState = stateFromStateString(stateString);
441             if ((state == SyncItemState.UNKNOWN) || (fileState == state)) {
442                 fileTimestamp = lastModifiedFromStateString(stateString);
443                 if (fileTimestamp > sinceTimestamp ) {
444                     syncItem = new SyncItemImpl(this, fileName, fileState);
445                     if (encode){
446                         syncItem.setProperty(
447                             new SyncItemProperty(SyncItem.PROPERTY_BINARY_CONTENT ,
448                                                  Base64.encode(readFileContent(fileName)))
449                         );
450                     } else {
451                         syncItem.setProperty(
452                             new SyncItemProperty(SyncItem.PROPERTY_BINARY_CONTENT,
453                                                  readFileContent(fileName) )
454                         );
455                     }
456                     syncItems.addElement(syncItem);
457                 }
458             }
459         } // next e
460

461         SyncItem[] ret = new SyncItem[syncItems.size()];
462         for (int i=0; i<ret.length; ++i) {
463             ret[i] = (SyncItem)syncItems.elementAt(i);
464         }
465
466         return ret;
467     }
468
469     /**
470      * Reads the content of the given file.
471      *
472      * @param fileName the name of the file to read
473      *
474      * @return the file content as a byte[]
475      */

476     protected byte[] readFileContent(String JavaDoc fileName) {
477         byte buf[] = null;
478
479         try {
480             File file = new File(sourceDirectory, fileName);
481
482             if (!file.exists()) return new byte[0];
483
484             buf = new byte[(int)file.length()];
485
486             FileInputStream fis = new FileInputStream(file);
487             fis.read(buf);
488             fis.close();
489         } catch (IOException e) {
490             buf = new byte[0];
491             e.printStackTrace();
492         }
493
494         return buf;
495     }
496
497     /**
498      * remove old deleted files from syncDB
499      */

500     private void restoreSyncDB() throws SyncException {
501
502         Properties JavaDoc syncDatabase = new Properties JavaDoc();
503
504         String JavaDoc state = null;
505         String JavaDoc fileName = null;
506         
507         try {
508
509             File fileSyncDatabase = getDatabaseFile(null);
510
511             //
512
// Reads the existing database
513
//
514
if (fileSyncDatabase.exists()) {
515                 FileInputStream fis = new FileInputStream(fileSyncDatabase);
516                 syncDatabase.load(fis);
517                 fis.close();
518             }
519
520             //
521
// Get the list of the file in the databsae
522
//
523
Enumeration JavaDoc databaseFiles = syncDatabase.propertyNames();
524
525             for (; databaseFiles.hasMoreElements();) {
526                 fileName = (String JavaDoc)databaseFiles.nextElement();
527
528                 state = syncDatabase.getProperty(fileName);
529
530                 if (stateFromStateString(state) == SyncItemState.DELETED) {
531                     syncDatabase.remove(fileName);
532                 }
533             } // next
534

535
536             //
537
// Save & return
538
//
539
FileOutputStream fos = new FileOutputStream(fileSyncDatabase);
540             syncDatabase.save(fos, DATABASE_HEADER);
541             fos.close();
542         } catch (IOException e) {
543             throw new SyncException( "Error reading sync database: "
544                                    + e.getMessage());
545         }
546     }
547 }
Popular Tags