source: contrib/funambol/trunk/modules/psync/src/main/java/br/com/prognus/psync/engine/source/PIMContactSyncSource.java @ 2082

Revision 2082, 17.4 KB checked in by emersonfaria, 14 years ago (diff)

Ticket #927 - Reestruturacao dos diretorios do Funambol

Line 
1/**
2 *
3 * @author Diorgenes Felipe Grzesiuk <diorgenes@prognus.com.br>
4 * @copyright Copyright 2007-2008 Prognus
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Foobar; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19package br.com.prognus.psync.engine.source;
20
21import java.io.ByteArrayInputStream;
22import java.sql.Timestamp;
23import java.util.List;
24
25import br.com.prognus.psync.exception.EntityException;
26import br.com.prognus.psync.items.manager.PIMContactManager;
27
28import com.funambol.common.pim.contact.Contact;
29import com.funambol.common.pim.converter.ContactToSIFC;
30import com.funambol.common.pim.converter.ContactToVcard;
31import com.funambol.common.pim.sif.SIFCParser;
32import com.funambol.common.pim.vcard.VcardParser;
33import com.funambol.framework.engine.SyncItem;
34import com.funambol.framework.engine.SyncItemImpl;
35import com.funambol.framework.engine.SyncItemKey;
36import com.funambol.framework.engine.SyncItemState;
37import com.funambol.framework.engine.source.SyncContext;
38import com.funambol.framework.engine.source.SyncSourceException;
39import com.funambol.framework.tools.beans.BeanInitializationException;
40
41public class PIMContactSyncSource extends PIMSyncSource {
42
43        // --------------------------------------------------------------Private
44        // data
45
46        private transient PIMContactManager manager;
47
48        // ------------------------------------------------------------Public
49        // methods
50
51        @Override
52        public void beginSync(SyncContext context) {
53
54                this.manager = new PIMContactManager(JNDI_DATA_SOURCE_NAME, context
55                                .getPrincipal());
56                super.manager = this.manager;
57                super.beginSync(context);
58        }
59
60        /**
61         * Makes an array of SyncItemKey objects representing the ID(s) of the
62         * twin(s) of a given contact.
63         *
64         * @param syncItem
65         *            the SyncItem representing the contact whose twin(s) are looked
66         *            for
67         * @throws SyncSourceException
68         * @return possibly, just one or no key should be in the array, but it can't
69         *         be ruled out a priori that several keys get returned by this
70         *         method
71         */
72        @Override
73        public SyncItemKey[] getSyncItemKeysFromTwin(SyncItem syncItem)
74                        throws SyncSourceException {
75
76                try {
77                        List idList = this.manager.getTwins(convert(syncItem));
78                        SyncItemKey[] keyList = new SyncItemKey[idList.size()];
79                        for (int i = 0; i < idList.size(); i++) {
80                                keyList[i] = new SyncItemKey(idList.get(i));
81                        }
82                        return keyList;
83                } catch (EntityException e) {
84                        throw new SyncSourceException("Error retrieving twin item keys.", e);
85                }
86        }
87
88        /**
89         * Adds a SyncItem object (representing a contact).
90         *
91         * @param syncItem
92         *            the SyncItem representing the contact
93         *
94         * @return a newly created syncItem based on the input object but with its
95         *         status set at SyncItemState.NEW and the GUID retrieved by the
96         *         back-end
97         */
98        @Override
99        public SyncItem addSyncItem(SyncItem syncItem) throws SyncSourceException {
100
101                if (log.isTraceEnabled()) {
102                        log.trace("PIMContactSyncSource addSyncItem begin");
103                }
104
105                Contact c = null;
106                String content = null;
107
108                try {
109
110                        content = getContentFromSyncItem(syncItem);
111                        String contentType = syncItem.getType();
112
113                        c = convert(content, contentType);
114                        Timestamp ts = syncItem.getTimestamp();
115
116                        // Adds the contact, wraps it in sync information and uses it to
117                        // create a new SyncItem which is the return value of this method
118                        SyncItemImpl newSyncItem = new SyncItemImpl(this, // syncSource
119                                        this.manager.addItem(c, ts), // key
120                                        null, // mappedKey
121                                        SyncItemState.NEW, // state
122                                        content.getBytes(), // content
123                                        null, // format
124                                        contentType, // type
125                                        ts // timestamp
126                        );
127
128                        return newSyncItem;
129
130                } catch (Exception e) {
131                        log.error("SyncSource error adding a new synchronization item", e);
132                        throw new SyncSourceException("Error adding the item " + syncItem,
133                                        e);
134                }
135        }
136
137        /**
138         * Updates a SyncItem object (representing a contact).
139         *
140         * @param syncItem
141         *            the SyncItem representing the contact
142         *
143         * @return a newly created syncItem based on the input object but with its
144         *         status set at SyncItemState.UPDATED and the GUID retrieved by the
145         *         back-end
146         */
147        @Override
148        public SyncItem updateSyncItem(SyncItem syncItem)
149                        throws SyncSourceException {
150
151                if (log.isTraceEnabled()) {
152                        log.trace("updateSyncItem from " + this.sourceURI);
153                }
154
155                Contact c = null;
156                String content = null;
157
158                try {
159
160                        String id = syncItem.getKey().getKeyAsString();
161                        content = getContentFromSyncItem(syncItem);
162                        String contentType = syncItem.getType();
163
164                        c = convert(content, contentType);
165
166                        // Modifies the contact, wraps it in sync information and uses it to
167                        // create a new SyncItem which is the return value of this method
168                        SyncItemImpl newSyncItem = new SyncItemImpl(this, // syncSource
169                                        this.manager.updateItem(id, c, syncItem.getTimestamp()), // key
170                                        null, // mappedKey
171                                        SyncItemState.UPDATED, // state
172                                        content.getBytes(), // content
173                                        null, // format
174                                        contentType, // type
175                                        null // timestamp
176                        );
177
178                        return newSyncItem;
179
180                } catch (Exception e) {
181                        log
182                                        .error(
183                                                        "SyncSource error updating a new synchronization item",
184                                                        e);
185                        throw new SyncSourceException(
186                                        "Error updating the item " + syncItem, e);
187                }
188        }
189
190        /**
191         * Deletes the item with a given syncItemKey.
192         *
193         * @param syncItemKey
194         * @param timestamp
195         *            in case of a soft deletion, this will be the registered moment
196         *            of deletion; if a hard deletion is used, this field is
197         *            irrelevant and may also be null
198         * @param softDelete
199         *            it is true if the client requires a soft deletion
200         * @throws SyncSourceException
201         */
202        @Override
203        public void removeSyncItem(SyncItemKey syncItemKey, Timestamp timestamp,
204                        boolean softDelete) throws SyncSourceException {
205
206                try {
207
208                        if (!softDelete) {
209                                if (log.isTraceEnabled()) {
210                                        log.trace("PIMContactSyncSource remove the SyncItem");
211                                }
212
213                                this.manager.removeItem(syncItemKey.getKeyAsString());
214
215                                if (log.isTraceEnabled()) {
216                                        log.trace("PIMContactSyncSource removed SyncItem");
217                                }
218                        }
219
220                } catch (EntityException e) {
221                        log.error("Sync source error: could not delete item with key"
222                                        + syncItemKey, e);
223                        throw new SyncSourceException("Error deleting item. ", e);
224                }
225
226        }
227
228        @Override
229        public SyncItem getSyncItemFromId(SyncItemKey syncItemKey)
230                        throws SyncSourceException {
231
232                String id = null;
233                SyncItem syncItem = null;
234
235                id = syncItemKey.getKeyAsString();
236
237                if (log.isTraceEnabled()) {
238                        log.trace("PIMContactSyncSource get SyncItem from id " + id);
239                }
240
241                try {
242
243                        // Retrieves the contact, wraps it in sync information and uses it
244                        // to create a new SyncItem which is the return value of this method
245                        syncItem = createSyncItem(id,
246                                        this.manager.getItem(id).getContact(), SyncItemState.NEW);
247
248                } catch (EntityException e) {
249                        throw new SyncSourceException("Error seeking SyncItem with ID: "
250                                        + id, e);
251                }
252
253                return syncItem;
254
255        }
256
257        public boolean mergeSyncItems(SyncItemKey syncItemKey, SyncItem syncItem)
258                        throws SyncSourceException {
259
260                try {
261
262                        Contact contact = convert(getContentFromSyncItem(syncItem),
263                                        syncItem.getType());
264
265                        boolean clientUpdateRequired = this.manager.mergeItems(syncItemKey
266                                        .getKeyAsString(), contact, syncItem.getTimestamp());
267
268                        if (clientUpdateRequired) {
269                                syncItem = getSyncItemFromId(syncItemKey);
270                        }
271
272                        // return clientUpdateRequired;
273                        return true;
274
275                } catch (EntityException e) {
276
277                        log.error("SyncSource error: a merge did not succeed.", e);
278                        throw new SyncSourceException("Error merging SyncItem with key '"
279                                        + syncItemKey + "' with SyncItem '" + syncItem + "'", e);
280                }
281        }
282
283        public void init() throws BeanInitializationException {
284        }
285
286        /**
287         * Makes an array of SyncItemKey objects representing all new contact IDs,
288         * filtered according to a given time interval.
289         *
290         * @param since
291         *            the earlier limit of the time interval
292         * @param to
293         *            the later limit of the time interval
294         * @return a SyncItemKey array
295         */
296        @Override
297        public SyncItemKey[] getNewSyncItemKeys(Timestamp since, Timestamp to)
298                        throws SyncSourceException {
299
300                saveSyncTiming(since, to);
301
302                try {
303                        List idList = this.manager.getNewItems(since, to);
304                        SyncItemKey[] keyList = new SyncItemKey[idList.size()];
305                        for (int i = 0; i < idList.size(); i++) {
306                                keyList[i] = new SyncItemKey(idList.get(i));
307                        }
308                        return keyList;
309                } catch (EntityException e) {
310                        throw new SyncSourceException("Error retrieving new item keys.", e);
311                }
312        }
313
314        /**
315         * Makes an array of SyncItemKey objects representing all deleted contact
316         * IDs, filtered according to a given time interval.
317         *
318         * @param since
319         *            the earlier limit of the time interval
320         * @param to
321         *            the later limit of the time interval
322         * @return a SyncItemKey array
323         */
324        public SyncItemKey[] getUpdatedSyncItemKeys(Timestamp since, Timestamp to)
325                        throws SyncSourceException {
326
327                saveSyncTiming(since, to);
328
329                try {
330                        List idList = this.manager.getUpdatedItems(since, to);
331                        SyncItemKey[] keyList = new SyncItemKey[idList.size()];
332                        for (int i = 0; i < idList.size(); i++) {
333                                keyList[i] = new SyncItemKey(idList.get(i));
334                        }
335                        return keyList;
336                } catch (EntityException e) {
337                        throw new SyncSourceException(
338                                        "Error retrieving updated item keys.", e);
339                }
340        }
341
342        /**
343         * Makes an array of SyncItemKey objects representing all deleted contact
344         * IDs, filtered according to a given time interval.
345         *
346         * @param since
347         *            the earlier limit of the time interval
348         * @param to
349         *            the later limit of the time interval
350         * @return a SyncItemKey array
351         */
352        @Override
353        public SyncItemKey[] getDeletedSyncItemKeys(Timestamp since, Timestamp to)
354                        throws SyncSourceException {
355
356                saveSyncTiming(since, to);
357
358                try {
359// Alteracao - Incluido parametro this.getSourceURI() - emerson-faria.nobre@serpro.gov.br - 23/09/09
360                        List idList = this.manager.getDeletedItems(since, to, this.getSourceURI());
361                        SyncItemKey[] keyList = new SyncItemKey[idList.size()];
362                        for (int i = 0; i < idList.size(); i++) {
363                                keyList[i] = new SyncItemKey(idList.get(i));
364                        }
365                        return keyList;
366                } catch (EntityException e) {
367                        throw new SyncSourceException(
368                                        "Error retrieving deleted item keys.", e);
369                }
370        }
371
372        /**
373         * Makes an array of SyncItemKey objects representing all contact IDs.
374         *
375         * @return a SyncItemKey array
376         */
377        @Override
378        public SyncItemKey[] getAllSyncItemKeys() throws SyncSourceException {
379
380                try {
381                        List idList = this.manager.getAllItems();
382                        SyncItemKey[] keyList = new SyncItemKey[idList.size()];
383                        for (int i = 0; i < idList.size(); i++) {
384                                keyList[i] = new SyncItemKey(idList.get(i));
385                        }
386                        return keyList;
387                } catch (EntityException e) {
388                        throw new SyncSourceException("Error retrieving all item keys. ", e);
389                }
390
391        }
392
393        // ---------------------------------------------------------- Private
394        // methods
395
396        private Contact sif2Contact(String sifc) throws EntityException {
397
398                if (log.isTraceEnabled()) {
399                        StringBuilder sb = new StringBuilder(sifc.length() + 60);
400                        sb.append("Converting: SIF-C => Contact").append("\nINPUT = {")
401                                        .append(sifc).append('}');
402                        log.trace(sb.toString());
403                }
404
405                ByteArrayInputStream buffer = null;
406                SIFCParser parser = null;
407                Contact contact = null;
408                try {
409                        contact = new Contact();
410                        buffer = new ByteArrayInputStream(sifc.getBytes());
411                        if ((sifc.getBytes()).length > 0) {
412                                parser = new SIFCParser(buffer);
413                                contact = parser.parse();
414                        }
415                } catch (Exception e) {
416                        throw new EntityException("Error converting SIF-C to Contact. ", e);
417                }
418
419                if (log.isTraceEnabled()) {
420                        log.trace("Conversion done.");
421                }
422                return contact;
423        }
424
425        private Contact vcard2Contact(String vcard) throws EntityException {
426
427                if (log.isTraceEnabled()) {
428                        StringBuilder sb = new StringBuilder(vcard.length() + 60);
429                        sb.append("Converting: VCARD => Contact").append("\nINPUT = {")
430                                        .append(vcard).append('}');
431                        log.trace(sb.toString());
432                }
433
434                ByteArrayInputStream buffer = null;
435                VcardParser parser = null;
436                Contact contact = null;
437                try {
438                        contact = new Contact();
439
440                        buffer = new ByteArrayInputStream(vcard.getBytes());
441                        if ((vcard.getBytes()).length > 0) {
442                                parser = new VcardParser(buffer,
443                                                this.deviceTimeZoneDescription, this.deviceCharset);
444                                contact = parser.vCard();
445                        }
446                } catch (Exception e) {
447                        throw new EntityException("Error converting VCARD to Contact. ", e);
448                }
449
450                if (log.isTraceEnabled()) {
451                        log.trace("Conversion done.");
452                }
453                return contact;
454        }
455
456        private String contact2sif(Contact contact) throws EntityException {
457
458                if (log.isTraceEnabled()) {
459                        log.trace("Converting: Contact => SIF-C");
460                }
461
462                String xml = null;
463                try {
464                        // this.deviceTimeZone, this.deviceCharset
465                        ContactToSIFC c2xml = new ContactToSIFC(null, null);
466                        xml = c2xml.convert(contact);
467
468                        if (log.isTraceEnabled()) {
469                                log.trace("OUTPUT = {" + xml + "}. Conversion done.");
470                        }
471                } catch (Exception e) {
472                        throw new EntityException("Error converting Contact to SIF-C. ", e);
473                }
474                return xml;
475        }
476
477        private String contact2vcard(Contact contact) throws EntityException {
478
479                if (log.isTraceEnabled()) {
480                        log.trace("Converting: Contact => VCARD");
481                }
482
483                String vcard = null;
484                try {
485                        ContactToVcard c2vc = new ContactToVcard(this.deviceTimeZone,
486                                        this.deviceCharset);
487                        vcard = c2vc.convert(contact);
488
489                        if (log.isTraceEnabled()) {
490                                log.trace("OUTPUT = {" + vcard + "}. Conversion done.");
491                        }
492                } catch (Exception e) {
493                        throw new EntityException("Error converting Contact to VCARD. ", e);
494                }
495                return vcard;
496        }
497
498        /**
499         * Create a new SyncItem from a Contact. The target contentType and status
500         * are passed as arguments.
501         *
502         * @param contact
503         *            the Contact object representing the input information
504         * @param contentType
505         *            chosen among the TYPE array's elements
506         * @param status
507         * @throws EntityException
508         *             if the content type is wrong or any problem occurs while
509         *             creating a new SyncItem
510         * @return a newly created SyncItem object
511         */
512        private SyncItem createSyncItem(String id, Contact contact, char status)
513                        throws EntityException {
514
515                String contentType = getInfo().getPreferredType().getType();
516
517                if (log.isTraceEnabled()) {
518                        StringBuilder sb = new StringBuilder(100);
519                        sb.append("PIMCalendarSyncSource - creating item with:").append(
520                                        "\n> id: ").append(id).append("\n> status: ")
521                                        .append(status).append("\n> content-type: ").append(
522                                                        contentType);
523                        log.trace(sb.toString());
524                }
525
526                SyncItem syncItem = null;
527                String stream = convert(contact, contentType);
528
529                try {
530                        syncItem = new SyncItemImpl(this, id, status);
531                } catch (Exception e) {
532                        throw new EntityException(e);
533                }
534
535                syncItem.setType(contentType);
536                syncItem.setContent(stream.getBytes());
537
538                if (log.isTraceEnabled()) {
539                        log.trace("PIMContactSyncSource created SyncItem");
540                }
541
542                return syncItem;
543        }
544
545        /**
546         * Converts a SyncItem to a Contact object, provided it represents a contact
547         * item in VCard or SIF-C format.
548         *
549         * @param syncItem
550         * @throws EntityException
551         *             if the contentType is wrong or the conversion attempt doesn't
552         *             succeed.
553         * @return a Contact object
554         */
555        private Contact convert(SyncItem syncItem) throws EntityException {
556                return convert(getContentFromSyncItem(syncItem), syncItem.getType());
557        }
558
559        /**
560         * Converts a contact in vCard or SIF-C format to a Contact object.
561         *
562         * @param content
563         *            as a String
564         * @param contentType
565         * @throws EntityException
566         *             if the contentType is wrong or the conversion attempt doesn't
567         *             succeed.
568         * @return a Contact object
569         */
570        private Contact convert(String content, String contentType)
571                        throws EntityException {
572                // Finds out which target type is required
573                for (int i = 0; i < TYPE.length; i++) {
574                        if (contentType.equals(TYPE[i])) { // Bingo!
575
576                                // Uses the proper converter method
577                                switch (i) {
578                                case VCARD:
579                                        return vcard2Contact(content);
580                                case SIFC:
581                                        return sif2Contact(content);
582                                default:
583                                        throw new EntityException("Can't make a Contact "
584                                                        + "out of a " + TYPE[i] + "!");
585                                }
586                        }
587                }
588                throw new EntityException("Content type unknown: " + contentType);
589        }
590
591        /**
592         * Converts a Contact back to a streamable (vCard, SIF-C) format.
593         *
594         * @param contact
595         * @param contentType
596         * @throws EntityException
597         *             if the contentType is wrong or the conversion attempt doesn't
598         *             succeed.
599         * @return the result in the required format
600         */
601        private String convert(Contact contact, String contentType)
602                        throws EntityException {
603
604                // Finds out which target type is required
605                for (int i = 0; i < TYPE.length; i++) {
606                        if (contentType.equals(TYPE[i])) { // Bingo!
607
608                                // Uses the proper converter method
609                                switch (i) {
610                                case VCARD:
611                                        return contact2vcard(contact);
612                                case SIFC:
613                                        return contact2sif(contact);
614                                default:
615                                        throw new EntityException("Can't make a " + TYPE[i]
616                                                        + "out of a Contact!");
617                                }
618                        }
619                }
620                throw new EntityException("Content type unknown: " + contentType);
621        }
622}
Note: See TracBrowser for help on using the repository browser.