2 * ContactSyncSource.java
4 * Created on Oct 28, 2007, 12:13:41 PM
6 * GroupDAV connector for Funambol v6.5
7 * Copyright (C) 2007-2008 Mathew McBride
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 package net
.bionicmessage
.funambol
.groupdav
.contacts
;
24 //import com.funambol.foundation.pdi.contact.Contact;
25 import com
.funambol
.common
.pim
.contact
.Contact
;
26 import com
.funambol
.framework
.engine
.SyncItem
;
27 import com
.funambol
.framework
.engine
.SyncItemKey
;
28 import com
.funambol
.framework
.engine
.SyncItemState
;
29 import com
.funambol
.framework
.engine
.source
.AbstractSyncSource
;
30 import com
.funambol
.framework
.engine
.source
.SyncContext
;
31 import com
.funambol
.framework
.engine
.source
.SyncSourceException
;
32 import com
.funambol
.framework
.tools
.beans
.BeanInitializationException
;
33 import com
.funambol
.framework
.tools
.beans
.LazyInitBean
;
35 import java
.io
.Serializable
;
37 import java
.sql
.Timestamp
;
38 import java
.util
.ArrayList
;
39 import java
.util
.Hashtable
;
40 import java
.util
.Iterator
;
42 import java
.util
.Properties
;
43 import java
.util
.logging
.FileHandler
;
44 import java
.util
.logging
.Level
;
45 import java
.util
.logging
.Logger
;
46 import net
.bionicmessage
.funambol
.framework
.Constants
;
47 import net
.bionicmessage
.funambol
.framework
.Utilities
;
48 import net
.bionicmessage
.objects
.MultipleSourceVCardObjectStore
;
49 import net
.bionicmessage
.objects
.StoreConstants
;
50 import net
.bionicmessage
.utils
.HTMLFormatter
;
56 public class ContactSyncSource
extends AbstractSyncSource
implements LazyInitBean
, Serializable
{
58 private Properties connectorProperties
= null;
59 private MultipleSourceVCardObjectStore so
= null;
60 private FileHandler fh
= null;
61 private Logger log
= null;
62 private int syncType
= 0;
63 private Map itemStates
= null;
64 private Map sources
= null;
65 private Properties addrStoreProperties
= null;
66 private SyncContext ctx
;
68 public ContactSyncSource() {
71 public void beginSync(SyncContext syncContext
) throws SyncSourceException
{
72 log
= Logger
.getLogger(Constants
.CONTACT_LOGGER_NAME
+"-"+System
.currentTimeMillis());
73 log
.info("Begin sync for Contact Sync Source");
74 this.ctx
= syncContext
;
75 String principalName
= Utilities
.cleanFilePath(ctx
.getPrincipal().getName());
76 String storeDir
= connectorProperties
.getProperty(Constants
.STOREDIR_PATH
);
77 File stdir
= new File(storeDir
+ File
.separatorChar
+ principalName
+ File
.separatorChar
);
81 if (!stdir
.exists()) {
85 HTMLFormatter hf
= new HTMLFormatter();
86 if (connectorProperties
.getProperty(Constants
.SINGLE_LOG
) != null) {
87 fh
= new FileHandler(stdir
.toString() + File
.separatorChar
+ "connector.html");
88 storeopts
+= MultipleSourceVCardObjectStore
.OPTION_SINGLELOG
;
90 long curTime
= new java
.util
.Date().getTime();
91 fh
= new FileHandler(stdir
.toString() + File
.separatorChar
+ "connector-" + curTime
+ ".html");
95 log
.setLevel(Level
.ALL
);
97 itemStates
= new Hashtable();
98 syncType
= ctx
.getSyncMode();
99 log
.info("Beginning sync for " + principalName
+ " for mode=" + syncType
);
100 if (syncType
== 201 || syncType
== 205) {
103 } catch (Exception e
) {
105 log
.throwing("beginSync", "ContactSyncSource", e
);
106 throw new SyncSourceException(e
);
109 itemStates
= new Hashtable();
110 syncType
= ctx
.getSyncMode();
111 String creds
= ctx
.getPrincipal().getEncodedCredentials();
112 sources
= new Hashtable();
113 addrStoreProperties
= new Properties();
114 // Process the sources
115 Iterator configKeys
= connectorProperties
.keySet().iterator();
116 while (configKeys
.hasNext()) {
117 String keyName
= (String
) configKeys
.next();
118 if (keyName
.contains(Constants
.SOURCE_LOCATION_BASE
)) {
119 String sourceLocation
= connectorProperties
.getProperty(keyName
);
120 sourceLocation
= sourceLocation
.replace("%USER%", syncContext
.getPrincipal().getUsername());
121 String sourceName
= StoreConstants
.BASE_PROPERTY_SOURCE
.concat(keyName
.replace(Constants
.SOURCE_LOCATION_BASE
, ""));
122 addrStoreProperties
.setProperty(sourceName
, sourceLocation
);
125 String domainString
= connectorProperties
.getProperty(Constants
.SERVER_HOST
);
127 // Parse users email address
128 if (domainString
.contains(Constants
.DOMAIN_REPLACE
)) {
129 String emailAddr
= ctx
.getPrincipal().getUser().getUsername();
130 if (!emailAddr
.contains("@")) {
131 emailAddr
= ctx
.getPrincipal().getUser().getEmail();
133 String
[] emailComponenets
= emailAddr
.split("@");
134 String domain
= emailComponenets
[1];
135 domainString
= domainString
.replace(Constants
.DOMAIN_REPLACE
, domain
);
137 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_SERVER
,
139 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_STORE_LOCATION
, storeDir
);
140 String collectionMode
= connectorProperties
.getProperty(StoreConstants
.PROPERTY_SERVER_MODE
);
141 if (collectionMode
!= null)
142 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_SERVER_MODE
, collectionMode
);
143 String cthreads
= connectorProperties
.getProperty(StoreConstants
.PROPERTY_SERVER_CTHREADS
);
144 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_SERVER_CTHREADS
, cthreads
);
145 String citems
= connectorProperties
.getProperty(StoreConstants
.PROPERTY_SERVER_ITEMS
);
146 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_SERVER_ITEMS
, citems
);
148 so
= new MultipleSourceVCardObjectStore(stdir
.toString(), storeopts
);
149 so
.setProperties(addrStoreProperties
);
150 so
.loadSourcesFromProps();
152 so
.setServer(new URI(domainString
), creds
);
155 } catch (Exception e
) {
157 log
.throwing("ContactSyncSource", e
.getMessage(), e
);
158 throw new SyncSourceException(e
);
162 public SyncItemKey
[] getAllSyncItemKeys() {
163 log
.info("getAllSyncItemKeys()");
164 SyncItemKey
[] newKeys
= this.getNewSyncItemKeys(null, null);
165 SyncItemKey
[] updatedKeys
= this.getUpdatedSyncItemKeys(null, null);
166 SyncItemKey
[] allKnownKeys
= new SyncItemKey
[newKeys
.length
+ updatedKeys
.length
];
168 for (int i
= 0; i
< newKeys
.length
; i
++) {
169 allKnownKeys
[curKey
] = newKeys
[i
];
172 for (int i
= 0; i
< updatedKeys
.length
; i
++) {
173 allKnownKeys
[curKey
] = updatedKeys
[i
];
179 /** Lists all deleted items since last sync.
181 * @param sinceTs Not used
182 * @param untilTs Not used
183 * @throws com.funambol.framework.engine.source.SyncSourceException
185 public SyncItemKey
[] getDeletedSyncItemKeys(java
.sql
.Timestamp sinceTs
, java
.sql
.Timestamp untilTs
)
186 throws SyncSourceException
{
187 log
.info("getDeletedSyncItemKeys()");
188 ArrayList deletedUIDS
= so
.getDeletedFromStoreUIDS();
189 SyncItemKey
[] keys
= new SyncItemKey
[deletedUIDS
.size()];
190 for (int i
= 0; i
< deletedUIDS
.size(); i
++) {
191 String key
= (String
) deletedUIDS
.get(i
);
192 SyncItemKey sk
= new SyncItemKey(key
);
198 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
199 // #[regen=yes,id=DCE.EE4A388C-A94E-B384-EC8A-2E349EACBA40]
201 public SyncItemKey
[] getNewSyncItemKeys(java
.sql
.Timestamp sinceTs
, java
.sql
.Timestamp untilTs
) {
202 log
.info("getNewSyncItemKeys()");
203 ArrayList newUIDS
= so
.getAddedToStoreUIDS();
204 SyncItemKey
[] keys
= new SyncItemKey
[newUIDS
.size()];
205 for (int i
= 0; i
< newUIDS
.size(); i
++) {
206 String key
= (String
) newUIDS
.get(i
);
207 SyncItemKey sk
= new SyncItemKey(key
);
209 itemStates
.put(sk
, "new");
214 public SyncItem
getSyncItemFromId(SyncItemKey syncItemKey
) throws SyncSourceException
{
215 String uid
= syncItemKey
.getKeyAsString();
216 log
.info("getSyncItemFromId(" + uid
+ ")");
218 Contact object
= so
.getObjectFromStore(uid
);
219 OutboundFunambolContactObject obc
= getOutboundProcessor(object
);
220 SyncItem si
= obc
.generateSyncItem(this);
221 si
.setState(getStateForItem(syncItemKey
));
222 si
.setType(this.getType());
224 } catch (Exception ex
) {
225 log
.log(Level
.SEVERE
, "getSyncItemFromId", ex
);
226 throw new SyncSourceException(ex
);
230 public void removeSyncItem(SyncItemKey syncItem
, Timestamp arg1
, boolean arg2
) throws SyncSourceException
{
231 log
.info("removeSyncItem(" + syncItem
.getKeyAsString() + ")");
233 so
.deleteObject(syncItem
.getKeyAsString());
234 } catch (Exception e
) {
235 throw new SyncSourceException(e
);
239 public SyncItem
updateSyncItem(SyncItem toUpdate
) throws SyncSourceException
{
240 log
.info("updateSyncItem(" + toUpdate
.getKey().getKeyAsString() + ")");
242 InboundFunambolContactObject ifcb
= initInboundProcessor(toUpdate
);
243 String output
= ifcb
.getStringRepresentation(Constants
.TYPE_CONTACT_VCARD21
);
244 String name
= ifcb
.getObjectName();
245 String uid
= ifcb
.getUid();
246 int status
= so
.replaceObject(ifcb
.getDestinationStore(),uid
, name
, output
);
247 return getSyncItemFromId(toUpdate
.getKey());
248 } catch (Exception e
) {
249 log
.info("Exception caught in updateSyncItem");
250 throw new SyncSourceException(e
);
254 public SyncItem
addSyncItem(SyncItem toAdd
) throws SyncSourceException
{
255 log
.info("addSyncItem(" + toAdd
.getKey().getKeyAsString() + ")");
257 InboundFunambolContactObject ifcb
= initInboundProcessor(toAdd
);
258 String output
= ifcb
.getStringRepresentation(Constants
.TYPE_CONTACT_VCARD21
);
259 String name
= ifcb
.getObjectName();
260 String uid
= ifcb
.getUid();
261 String srvUid
= so
.addObject(ifcb
.getDestinationStore(), uid
, name
, output
);
262 return getSyncItemFromId(new SyncItemKey(srvUid
));
263 } catch (Throwable e
) {
264 log
.info("Exception caught in addSyncItem");
265 throw new SyncSourceException(e
);
269 public SyncItemKey
[] getSyncItemKeysFromTwin(SyncItem syncItem
) throws SyncSourceException
{
270 log
.info("getSyncItemKeysFromTwin " + syncItem
.getKey().getKeyAsString());
271 String uid
= syncItem
.getKey().getKeyAsString();
272 String data
= new String(syncItem
.getContent());
274 InboundFunambolContactObject ifcb
= initInboundProcessor(syncItem
);
275 String name
= ifcb
.getObjectName();
277 // No name? Lets get out of here!
280 String match
= so
.searchUids(name
, 0,0);
282 log
.info("Match found: " + match
);
283 SyncItemKey
[] matches
= new SyncItemKey
[1];
284 matches
[0] = new SyncItemKey(match
);
287 } catch (Exception ex
) {
288 log
.info("Error cause data=" + data
);
289 log
.log(Level
.SEVERE
, "getSyncItemKeysFromTwin", ex
);
290 throw new SyncSourceException(ex
);
295 public void setOperationStatus(String reason
, int status
, SyncItemKey
[] keys
) {
296 for (int i
= 0; i
< keys
.length
; i
++) {
297 SyncItemKey syncItemKey
= keys
[i
];
298 log
.info("setOperationStatus(" + reason
+ "," + status
+ "," + syncItemKey
.getKeyAsString() + ")");
304 public void endSync() throws SyncSourceException
{
305 log
.info("endSync()");
309 } catch (Exception e
) {
310 log
.info("Exception caught in endSync()");
311 throw new SyncSourceException(e
);
317 * Called to get the keys of the items updated in the time frame sinceTs - untilTs.
318 * <br><code>sinceTs</code> null means all keys of the items updated until <code>untilTs</code>.
319 * <br><code>untilTs</code> null means all keys of the items updated since <code>sinceTs</code>.
320 * @param sinceTs consider the changes since this point in time.
321 * @param untilTs consider the changes until this point in time.
322 * @return an array of keys containing the <code>SyncItemKey</code>'s key of the updated
323 * items in the given time frame. It MUST NOT return null for
324 * no keys, but instad an empty array.
326 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
327 // #[regen=yes,id=DCE.A80FDB1D-9EC6-0537-6211-5EC359BAAB6D]
329 public SyncItemKey
[] getUpdatedSyncItemKeys(Timestamp sinceTs
, Timestamp untilTs
) {
330 log
.info("getUpdatedSyncItemKeys()");
331 ArrayList updatedKeys
= so
.getUpdatedInStoreUIDS();
332 SyncItemKey
[] keys
= new SyncItemKey
[updatedKeys
.size()];
333 for (int i
= 0; i
< updatedKeys
.size(); i
++) {
334 String key
= (String
) updatedKeys
.get(i
);
335 SyncItemKey sk
= new SyncItemKey(key
);
337 itemStates
.put(sk
, "updated");
342 public void init() throws BeanInitializationException
{
345 private OutboundFunambolContactObject
getOutboundProcessor(Contact origObject
) throws Exception
{
346 OutboundFunambolContactObject obc
= new OutboundFunambolContactObject();
347 obc
.setContactObject(origObject
);
351 private char getStateForItem(SyncItemKey sik
) {
352 String state
= (String
) itemStates
.get(sik
);
354 return SyncItemState
.SYNCHRONIZED
;
355 } else if (state
.equals("new")) {
356 return SyncItemState
.NEW
;
357 } else if (state
.equals("updated")) {
358 return SyncItemState
.UPDATED
;
360 return SyncItemState
.UNKNOWN
;
363 public void setConnectorProperties(Properties p
) {
364 connectorProperties
= p
;
367 public Properties
getConnectorProperties() {
368 return connectorProperties
;
371 public SyncContext
getSyncContext() {
375 /** Create the inbound object processing class (default is
376 * InboundFunambolContactObject
377 * @see InboundFunambolContactObject
379 private InboundFunambolContactObject
initInboundProcessor(SyncItem si
) throws Exception
{
380 InboundFunambolContactObject ifcb
;
381 ifcb
= new InboundFunambolContactObject();
382 ifcb
.setSyncSource(this);
383 ifcb
.setOriginalSyncItem(si
);