KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > syncclient > spds > source > SPSSyncSource


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.spds.source;
20
21 import java.security.Principal JavaDoc;
22 import java.util.Date JavaDoc;
23 import java.util.Enumeration JavaDoc;
24 import java.util.Hashtable JavaDoc;
25 import java.util.Vector JavaDoc;
26
27 import sync4j.framework.core.AlertCode;
28
29 import sync4j.syncclient.sps.common.*;
30
31 import sync4j.syncclient.spds.engine.*;
32 import sync4j.syncclient.spds.SyncRecordFunction;
33 import sync4j.syncclient.spds.SyncException;
34
35 /**
36  * This class implements a <i>SPSSyncSource</i>.
37  * It uses SPS API.
38  *
39  * @author Fabio Maggi
40  *
41  * @version $Id: SPSSyncSource.java,v 1.3 2005/01/19 11:18:36 fabius Exp $
42  *
43  */

44 public class SPSSyncSource implements SyncSource {
45
46     private String JavaDoc name = null;
47     private String JavaDoc type = null;
48     private String JavaDoc sourceClass = null;
49     private String JavaDoc sourceURI = null;
50     private String JavaDoc storeManager = null;
51     private String JavaDoc storeManagerPackage = null;
52     private String JavaDoc storeVolume = null;
53
54     private int syncMode;
55
56     /** enable / disable source syncronization */
57     private boolean sync = false;
58
59     /** set true if source changed */
60     /** if it is false, no write dataStore to handle */
61     private boolean syncSourceChanged = false;
62
63     private boolean sortDB = false;
64
65     /** creator id of handle application */
66     private String JavaDoc creatorId = null;
67
68     /** data type of handle database */
69     private String JavaDoc dataStoreType = null;
70
71     private DataStoreManager dataStoreManager = null;
72
73     private DataStore dataStore = null;
74
75     private ClassLoader JavaDoc classLoader = null;
76
77
78     // ------------------------------------------------------------ Constructors
79

80     /** Creates a new instance of SPSSyncSource */
81     public SPSSyncSource() {
82
83     }
84
85     // ---------------------------------------------------------- Public methods
86

87     public String JavaDoc getName() {
88         return this.name;
89     }
90
91     public void setName(String JavaDoc name) {
92         this.name = name;
93     }
94
95     public String JavaDoc getType() {
96         return this.type;
97     }
98
99     public void setType(String JavaDoc type) {
100         this.type = type;
101     }
102
103
104     /** Getter for property creatorId
105      * @return Value of creator id
106      */

107     public String JavaDoc getCreatorId() {
108         return this.creatorId;
109     }
110
111     /** Setter for property creatorId.
112      * @param creatorId New value of property creator id.
113      */

114     public void setCreatorId(String JavaDoc creatorId) {
115         this.creatorId = creatorId;
116     }
117
118     /** Getter for property dataStoreType
119      * @return Value of data store type
120      */

121     public String JavaDoc getdataStoreType() {
122         return this.dataStoreType;
123     }
124
125     /** Setter for property dataStoreType.
126      * @param dataStoreType New value of property data store sype.
127      */

128     public void setDataStoreType(String JavaDoc dataStoreType) {
129         this.dataStoreType = dataStoreType;
130     }
131
132     /** Getter for property sourceClass.
133      * @return Value of property sourceClass.
134      */

135     public String JavaDoc getSourceClass() {
136         return this.sourceClass;
137     }
138
139     /** Setter for property sourceClass.
140      * @param sourceClass New value of property sourceClass.
141      */

142     public void setSourceClass(String JavaDoc sourceClass) {
143         this.sourceClass = sourceClass;
144     }
145
146     /** Getter for property uri.
147      * @return Value of property uri.
148      */

149     public String JavaDoc getSourceURI() {
150         return this.sourceURI;
151     }
152
153     /** Setter for property uri.
154      * @param sourceURI New value of property sourceURI.
155      */

156     public void setSourceURI(String JavaDoc sourceURI) {
157
158         this.sourceURI = sourceURI;
159     }
160
161     /** Getter for property sortDB.
162      * @return Value of property sortDB.
163      */

164     public boolean getSortDB() {
165         return this.sortDB;
166     }
167
168     /** Setter for property sortDB.
169      * @param sortDB New value of property sortDB.
170      */

171     public void setSortDB(String JavaDoc sortDB) {
172
173         if ((sortDB).equals("true")) {
174             this.sortDB = true;
175         } else {
176             this.sortDB = false;
177         }
178
179     }
180
181     /** Getter for property storeManager.
182      * @return Value of property storeManager.
183      */

184     public String JavaDoc getStoreManager() {
185         return this.storeManager;
186     }
187
188     /** Setter for property storeManager.
189      * @param storeManager New value of property storeManager.
190      */

191     public void setStoreManager(String JavaDoc storeManager) {
192         this.storeManager = storeManager;
193     }
194
195     /** Getter for property storeManagerPackage.
196      * @return Value of property storeManagerPackage.
197      */

198     public String JavaDoc getStoreManagerPackage() {
199         return this.storeManagerPackage;
200     }
201
202     /** Setter for property storeManagerPackage.
203      * @param storeManagerPackage New value of property storeManagerPackage.
204      */

205     public void setStoreManagerPackage(String JavaDoc storeManagerPackage) {
206         this.storeManagerPackage = storeManagerPackage;
207     }
208
209
210     /** Getter for property classLoader.
211      * @return Value of property classLoader.
212      */

213     public ClassLoader JavaDoc getClassLoader() {
214         return this.classLoader;
215     }
216
217     /** Setter for property classLoader.
218      * @param classLoader New value of property classLoader.
219      */

220     public void setClassLoader(ClassLoader JavaDoc classLoader) {
221         this.classLoader = classLoader;
222     }
223
224     /** Getter for property sync.
225      * @return Value of property sync.
226      */

227     public boolean getSync() {
228         return this.sync;
229     }
230
231     /** Setter for property sync.
232      * @param sync New value of property sync.
233      */

234     public void setSync(String JavaDoc sync) {
235
236         if ((sync).equals("true")) {
237             this.sync = true;
238         } else {
239             this.sync = false;
240         }
241
242     }
243
244     /** Getter for property storeVolume.
245      * @return Value of property storeVolume.
246      */

247     public String JavaDoc getStoreVolume() {
248         return this.storeVolume;
249     }
250
251     /** Setter for property sync.
252      * @param storeVolume New value of property storeVolume.
253      */

254     public void setStoreVolume(String JavaDoc storeVolume) {
255         this.storeVolume = storeVolume;
256     }
257
258     // -------------------------------------------------------------------------
259

260     /**
261      * Returns a string representation of this object.
262      *
263      * @return a string representation of this object.
264      */

265     public String JavaDoc toString() {
266         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(super.toString());
267
268         sb.append(" - {name: ").append(getName() );
269         sb.append(" type: " ).append(getType() );
270         sb.append(" uri: " ).append(getSourceURI());
271         sb.append("}" );
272
273         return sb.toString();
274     }
275
276     /**
277      * Returns all items in the data store belonging to the given principal.
278      *
279      * @param principal the principal for which the data has to be considered
280      * Null means all principals
281      *
282      * @return an array of all <i>SyncItem</i>s stored in this source. If there
283      * are no items an empty array is returned.
284      *
285      * @throws SyncException in case of error (for instance if the
286      * underlying data store runs into problems)
287      */

288     public SyncItem[] getAllSyncItems(Principal JavaDoc principal) throws SyncException {
289         Vector JavaDoc records = null;
290
291         try {
292             records = dataStore.findAllRecords();
293         } catch (DataAccessException e) {
294             throw new SyncException(e.getMessage());
295         }
296
297         return recordsToSyncItems(dataStore, records);
298     }
299
300     /**
301      * Returns all deleted items belonging to the given principal and deleted
302      * after the given point in time.
303      *
304      * @param principal the principal for which the data has to be considered
305      * Null means all principals
306      * @param since consider the changes since this point in time. Null means
307      * all items regardless when they were changed.
308      *
309      * @return an array of keys containing the <i>SyncItem</i>'s key of the deleted
310      * items after the last synchronizazion. If there are no deleted
311      * items an empty array is returned.
312      *
313      * @throws SyncException in case of error (for instance if the
314      * underlying data store runs into problems)
315      */

316     public SyncItem[] getDeletedSyncItems(Principal JavaDoc principal,
317                                           Date JavaDoc since ) throws SyncException {
318         Vector JavaDoc records = null;
319
320         try {
321             records = dataStore.findRecords('D', since);
322         } catch (DataAccessException e) {
323             throw new SyncException(e.getMessage());
324         }
325
326         return recordsToSyncItems(dataStore, records);
327
328     }
329
330     /**
331      * Returns all new items belonging to the given principal and created
332      * after the given point in time.
333      *
334      * @param principal the principal for which the data has to be considered
335      * Null means all principals
336      * @param since consider the changes since this point in . Null means
337      * all items regardless when they were changed.
338      *
339      * @return an array of items containing representing the newly created items.
340      * If there are no new items an empty array MUST BE returned.
341      *
342      * @throws SyncException in case of error (for instance if the
343      * underlying data store runs into problems)
344      */

345     public SyncItem[] getNewSyncItems(Principal JavaDoc principal,
346                                       Date JavaDoc since ) throws SyncException {
347         Vector JavaDoc records = null;
348
349         try {
350             records = dataStore.findRecords('N', since);
351         } catch (DataAccessException e) {
352             throw new SyncException(e.getMessage());
353         }
354
355         return recordsToSyncItems(dataStore, records);
356     }
357
358     /**
359      * Returns all updated items belonging to the given principal and modified
360      * after the given point in time.
361      *
362      * @param principal the principal for which the data has to be considered
363      * Null means all principals
364      * @param since consider the changes since this point in time. Null means
365      * all items regardless when they were changed.
366      *
367      * @return an array of keys containing the <i>SyncItem</I>'s key of the updated
368      * items after the last synchronizazion. It MUST NOT return null for
369      * no keys, but instad an empty array.
370      */

371     public SyncItem[] getUpdatedSyncItems(Principal JavaDoc principal,
372                                           Date JavaDoc since ) throws SyncException {
373         Vector JavaDoc records = null;
374
375         try {
376             records = dataStore.findRecords('U', since);
377         } catch (DataAccessException e) {
378             throw new SyncException(e.getMessage());
379         }
380
381         return recordsToSyncItems(dataStore, records);
382
383     }
384
385     /**
386      * Removes a SyncItem given its key.
387      *
388      * @param principal the entity that wants to do the operation
389      * @param syncItem the item to remove
390      *
391      * @throws SyncException in case of error (for instance if the
392      * underlying data store runs into problems)
393      */

394     public void removeSyncItem(Principal JavaDoc principal, SyncItem syncItem) throws SyncException {
395
396         syncSourceChanged = true;
397
398         Record record = null;
399
400         record = syncItemToRecord (dataStore, syncItem);
401
402         try {
403             dataStore.deleteRecord(record);
404         } catch (DataAccessException e) {
405             throw new SyncException(e.getMessage());
406         }
407
408     }
409
410     /**
411      * Replaces an existing <i>SyncItem</i> or adds a new <i>SyncItem</i> if it
412      * does not exist. The item is also returned giving the opportunity to the
413      * source to modify its content and return the updated item (i.e. updating
414      * the id to the GUID).
415      *
416      * @param principal the entity that wants to do the operation
417      * @param syncItem the item to replace/add
418      *
419      * @return the inserted/updated item
420      *
421      * @throws SyncException in case of error (for instance if the
422      * underlying data store runs into problems)
423      */

424     public SyncItem setSyncItem(Principal JavaDoc principal, SyncItem syncItem) throws SyncException {
425
426         syncSourceChanged = true;
427
428         Record record = null;
429
430         long keyBase = 0;
431
432         record = syncItemToRecord (dataStore, syncItem);
433
434         try {
435             record = dataStore.storeRecord(record);
436         } catch (DataAccessException e) {
437             throw new SyncException(e.getMessage());
438         }
439
440         return recordToSyncItem(dataStore, record);
441
442     }
443
444     /**
445      * Method implements start sync operations
446      */

447     public void beginSync(int syncMode) throws SyncException {
448
449         this.syncMode = syncMode;
450
451         Hashtable JavaDoc dataStoreProperties = null;
452
453         dataStoreManager =
454             DataStoreManagerFactory.getDataStoreManager(
455                 storeManager,
456                 storeManagerPackage,
457                 classLoader
458             );
459
460         if (dataStoreManager == null) {
461            throw new SyncException( "Datastore manager "
462                                   + storeManagerPackage
463                                   + '.'
464                                   + storeManager
465                                   + DataStoreManagerFactory.DATASTORE_MANAGER_FILE_NAME_END
466                                   + " not found"
467                                   );
468         }
469
470         //
471
// We get the datastore name from the sourceURI as the trailing part
472
// after '/'
473
//
474
String JavaDoc storeName = sourceURI;
475         int p = storeName.lastIndexOf('/');
476         if ((p>=0) && (p<(storeName.length()-1))) {
477             storeName = sourceURI.substring(p+1);
478         }
479
480         dataStore = dataStoreManager.getDataStore(storeName);
481
482         if (dataStore == null) {
483             throw new SyncException("Datastore " + storeName + " not found!");
484         }
485
486         dataStoreProperties = new Hashtable JavaDoc();
487
488         if (this.creatorId != null) {
489             dataStoreProperties.put("creatorId", this.creatorId);
490         }
491
492         if (this.dataStoreType != null) {
493             dataStoreProperties.put("dataStoreType", this.dataStoreType);
494         }
495
496         if (this.storeVolume != null) {
497             dataStoreProperties.put("dataStoreVolume", this.storeVolume);
498         }
499
500         dataStore.setDataStoreProperties(dataStoreProperties);
501
502         try {
503             dataStore.startDBOperations();
504         } catch (DataAccessException e) {
505             throw new SyncException(e.getMessage());
506         }
507
508         //
509
// if the sync is a REFRESH_FROM_SERVER, the records currently in the
510
// data store must be deleted
511
//
512
if (syncMode == AlertCode.REFRESH_FROM_SERVER) {
513             try {
514                 emptyDataStore();
515             } catch (DataAccessException e) {
516                 throw new SyncException(e.getMessage());
517             }
518         }
519     }
520
521     /**
522      * Method implements end sync operations
523      */

524     public void commitSync() throws SyncException {
525
526         Vector JavaDoc records = null;
527
528         //
529
// If the database must be sorted, we sort it and we rewrite import to
530
// the datastore
531
//
532
if (syncSourceChanged && sortDB) {
533             try {
534                 records = (Vector JavaDoc) dataStore.findAllRecords().clone();
535
536                 int l = records.size();
537
538                 for (int i=0; i < l; i++) {
539                     dataStore.deleteRecord((Record) records.elementAt(i));
540                 }
541
542                 records = sortRecords(records);
543
544                 l = records.size();
545
546                 for (int i=0; i < l; i++) {
547                     dataStore.storeRecord((Record) records.elementAt(i));
548                 }
549
550                 records = null;
551
552             } catch (DataAccessException e) {
553                 throw new SyncException(e.getMessage());
554             }
555         }
556
557         try {
558             dataStore.commitDBOperations();
559         } catch (DataAccessException e) {
560             throw new SyncException(e.getMessage());
561         }
562
563         dataStore = null;
564
565         return;
566
567     }
568
569     // ---------------------------------------------------------- Private method
570

571     /**
572      * Convert a Vector of Record into Array of SyncItem.
573      *
574      * @param dataStore
575      * @param records
576      *
577      * @return an Array of SyncItem
578      *
579      */

580     private SyncItem[] recordsToSyncItems(DataStore dataStore, Vector JavaDoc records) {
581         SyncItem[] syncItems = null;
582
583         syncItems = new SyncItem[records.size()];
584
585         int l = records.size();
586
587         for (int i=0; i < l; i++) {
588             syncItems[i] = recordToSyncItem(dataStore, (Record) records.elementAt(i));
589         }
590
591         return syncItems;
592     }
593
594     /**
595      * Convert a Record into a SyncItem.
596      *
597      * @param dataStore
598      * @param record
599      *
600      * @return a SyncItem
601      *
602      */

603     private SyncItem recordToSyncItem(DataStore dataStore, Record record) {
604         StringBuffer JavaDoc xml = new StringBuffer JavaDoc();
605
606         int positionKey = 0;
607         int positionTimestamp = 0;
608         int positionState = 0;
609
610         int count = 0;
611
612         positionKey = record.getPositionKeyField();
613         positionTimestamp = SyncRecordFunction.getPositionTimestampField(record);
614         positionState = SyncRecordFunction.getPositionModificationTypeField(record);
615
616         //
617
// The content is stored in the <i>SyncItem.PROPERTY_BINARY_CONTENT</i>
618
// content as XML (see the design document for details on the XML schema).
619
//
620
SyncItem syncItem = null;
621
622         syncItem = new SyncItemImpl(
623            this,
624            record.getKey(),
625            record.getString(positionState).charAt(0)
626         );
627
628         //
629
// XML-ize the columns ignoring key, timestamp and state
630
//
631
xml.append("<record xmlns=\"xmlns://www.funambol.com/spds\">\n");
632
633
634         int l = dataStore.getRecordMetadata().getFieldMetadata().length;
635
636         for (int i=1; i <= l; i++) {
637
638             if (i != positionTimestamp && i != positionState) {
639
640                 if (record.getString(i)!=null && record.getString(i).length() > 0) {
641                     xml.append("<field" + i + ">" );
642                     xml.append(record.getString(i));
643                     xml.append("</field" + i + ">\n" );
644                 }
645
646             }
647
648         }
649
650         xml.append("</record>\n");
651
652         syncItem.setProperty(
653             new SyncItemProperty(SyncItem.PROPERTY_BINARY_CONTENT ,
654                              xml.toString().getBytes())
655         );
656
657         return syncItem;
658
659     }
660
661     /**
662      * Convert a SyncItem to Record
663      *
664      * @param dataStore
665      * @param syncItem
666      *
667      * @return a Record
668      *
669      */

670     private Record syncItemToRecord (DataStore dataStore, SyncItem syncItem) throws SyncException {
671
672         Record record = null;
673
674         Hashtable JavaDoc fields = null;
675         Enumeration JavaDoc fieldNumbers = null;
676
677         String JavaDoc xml = null;
678         String JavaDoc timestamp = null;
679         String JavaDoc fieldNumber = null;
680         String JavaDoc fieldValue = null;
681
682         int positionKey = 0;
683         int positionTimestamp = 0;
684         int positionState = 0;
685         int numberOperationFields = 0;
686
687         int j=0;
688
689         record = new Record (dataStore, syncItem.getKey().getKeyAsString());
690
691         positionKey = record.getPositionKeyField();
692         positionTimestamp = SyncRecordFunction.getPositionTimestampField(record);
693         positionState = SyncRecordFunction.getPositionModificationTypeField(record);
694         numberOperationFields = SyncRecordFunction.getNumberOperationFields(record);
695
696         if(syncItem.getPropertyValue(SyncItem.PROPERTY_BINARY_CONTENT) != null) {
697
698             xml = new String JavaDoc((byte[])syncItem.getPropertyValue(SyncItem.PROPERTY_BINARY_CONTENT));
699
700             fields = xmlToValues(xml);
701
702             fieldNumbers = fields.keys();
703
704             while (fieldNumbers.hasMoreElements()) {
705
706                 fieldNumber = (String JavaDoc) fieldNumbers.nextElement();
707                 fieldValue = (String JavaDoc) fields.get((Object JavaDoc)fieldNumber);
708
709                 try {
710                     record.setField(Integer.parseInt(fieldNumber), fieldValue);
711                 } catch (NumberFormatException JavaDoc e) {
712                     throw new SyncException("SPSSyncSource, error syncItemToRecord: " + e.getMessage());
713
714                 }
715             }
716
717             if (positionState !=-1 ) {
718                 record.setField(positionState, String.valueOf(syncItem.getState()));
719             }
720
721
722         }
723
724         return record;
725     }
726
727
728     /**
729      * Parse the XML content in the form
730      * <p>
731      * &lt;record&gt;
732      * &lt;field&gt;<i>value</i>&lt;/field&gt;
733      * ...
734      * &lt;/record&gt;
735      * <p>
736      * and return an hashtable of fieldNumber, fieldValue
737      *
738      * @param xml the xml string to parse
739      *
740      * @return the hashtable of fieldNumber, fieldValue
741      */

742     private Hashtable JavaDoc xmlToValues(String JavaDoc xml) {
743
744         Hashtable JavaDoc fields = null;
745         String JavaDoc fieldNumber = null;
746         String JavaDoc fieldValue = null;
747
748         int offset = 0, end, positionEndFieldTag, len;
749
750         fields = new Hashtable JavaDoc();
751
752         len = xml.length();
753
754         while (offset < len) {
755
756             offset = xml.indexOf("<field", offset);
757
758             if (offset < 0) {
759                 break;
760             }
761
762             offset += 6;
763
764             positionEndFieldTag = xml.indexOf(">", offset);
765
766             //case <fieldxxx/> (field void)
767
if (xml.substring(positionEndFieldTag - 1, positionEndFieldTag).equals("/")) {
768
769                 fieldNumber = xml.substring(offset, positionEndFieldTag - 1);
770                 fieldValue = "";
771                 offset = positionEndFieldTag + 1;
772
773             //case <fieldxxx>value</fieldxxx>
774
} else {
775
776                 fieldNumber = xml.substring(offset, positionEndFieldTag);
777                 end = xml.indexOf("</field" + fieldNumber + ">", positionEndFieldTag + 1);
778                 if (end < 0) {
779                     break;
780                 }
781
782                 fieldValue = xml.substring(positionEndFieldTag + 1, end);
783
784                 offset = end + fieldNumber.length() + 8;
785
786             }
787
788             fields.put(fieldNumber, fieldValue);
789         }
790
791         return fields;
792     }
793
794     /**
795      * Sorts the given records based on their keys.
796      *
797      * @param records to sort
798      *
799      * @return sort records
800      */

801     private Vector JavaDoc sortRecords(Vector JavaDoc records) {
802
803         Record tmp1Record = null;
804
805         Record tmp2Record = null;
806
807         for(int i = records.size() - 1 ; i > 0; i-- ) {
808
809             int maxLoc = 0;
810
811             for (int j = 1; j <= i; j++) {
812                 if (((Record) records.elementAt(j)).getKey().compareTo(((Record) records.elementAt(maxLoc)).getKey()) > 0) {
813                    maxLoc = j;
814                 }
815             }
816
817             tmp1Record = (Record) records.elementAt(maxLoc);
818
819             tmp2Record = (Record) records.elementAt(i);
820
821             records.setElementAt(tmp2Record, maxLoc);
822
823             records.setElementAt((Object JavaDoc) tmp1Record, i);
824
825         }
826
827         return records;
828     }
829
830     /**
831      * Empties the data store
832      *
833      * @throws DataAccessException
834      */

835     private void emptyDataStore() throws DataAccessException {
836
837         Vector JavaDoc records = (Vector JavaDoc)dataStore.findAllRecords().clone();
838
839         int l = records.size();
840
841         for (int i=0; i < l; i++) {
842             dataStore.deleteRecord((Record) records.elementAt(i));
843         }
844
845         records = null;
846
847     }
848 }
Popular Tags