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
;
36 import java
.sql
.Timestamp
;
37 import java
.util
.ArrayList
;
38 import java
.util
.Hashtable
;
39 import java
.util
.Iterator
;
41 import java
.util
.Properties
;
42 import java
.util
.logging
.FileHandler
;
43 import java
.util
.logging
.Level
;
44 import java
.util
.logging
.Logger
;
45 import net
.bionicmessage
.funambol
.framework
.Constants
;
46 import net
.bionicmessage
.funambol
.framework
.Utilities
;
47 import net
.bionicmessage
.objects
.MultipleSourceVCardObjectStore
;
48 import net
.bionicmessage
.objects
.StoreConstants
;
49 import net
.bionicmessage
.utils
.HTMLFormatter
;
55 public class ContactSyncSource
extends AbstractSyncSource
implements LazyInitBean
, Serializable
{
57 private Properties connectorProperties
= null;
58 private MultipleSourceVCardObjectStore so
= null;
59 private FileHandler fh
= null;
60 private Logger log
= null;
61 private int syncType
= 0;
62 private Map itemStates
= null;
63 private Map sources
= null;
64 private Properties addrStoreProperties
= null;
65 private SyncContext ctx
;
67 public ContactSyncSource() {
70 public void beginSync(SyncContext syncContext
) throws SyncSourceException
{
71 log
= Logger
.getLogger(Constants
.CONTACT_LOGGER_NAME
+"-"+System
.currentTimeMillis());
72 log
.info("Begin sync for Contact Sync Source");
73 this.ctx
= syncContext
;
74 String principalName
= Utilities
.cleanFilePath(ctx
.getPrincipal().getName());
75 String storeDir
= connectorProperties
.getProperty(Constants
.STOREDIR_PATH
);
76 File stdir
= new File(storeDir
+ File
.separatorChar
+ principalName
+ File
.separatorChar
);
80 if (!stdir
.exists()) {
84 HTMLFormatter hf
= new HTMLFormatter();
85 if (connectorProperties
.getProperty(Constants
.SINGLE_LOG
) != null) {
86 fh
= new FileHandler(stdir
.toString() + File
.separatorChar
+ "connector.html");
87 storeopts
+= MultipleSourceVCardObjectStore
.OPTION_SINGLELOG
;
89 long curTime
= new java
.util
.Date().getTime();
90 fh
= new FileHandler(stdir
.toString() + File
.separatorChar
+ "connector-" + curTime
+ ".html");
94 log
.setLevel(Level
.ALL
);
96 itemStates
= new Hashtable();
97 syncType
= ctx
.getSyncMode();
98 log
.info("Beginning sync for " + principalName
+ " for mode=" + syncType
);
99 if (syncType
== 201 || syncType
== 205) {
102 } catch (Exception e
) {
104 log
.throwing("beginSync", "ContactSyncSource", e
);
105 throw new SyncSourceException(e
);
108 itemStates
= new Hashtable();
109 syncType
= ctx
.getSyncMode();
110 String creds
= ctx
.getPrincipal().getEncodedCredentials();
111 sources
= new Hashtable();
112 addrStoreProperties
= new Properties();
113 // Process the sources
114 Iterator configKeys
= connectorProperties
.keySet().iterator();
115 while (configKeys
.hasNext()) {
116 String keyName
= (String
) configKeys
.next();
117 if (keyName
.contains(Constants
.SOURCE_LOCATION_BASE
)) {
118 String sourceLocation
= connectorProperties
.getProperty(keyName
);
119 sourceLocation
= sourceLocation
.replace("%USER%", syncContext
.getPrincipal().getUsername());
120 String sourceName
= MultipleSourceVCardObjectStore
.BASE_PROPERTY_SOURCE
.concat(keyName
.replace(Constants
.SOURCE_LOCATION_BASE
, ""));
121 addrStoreProperties
.setProperty(sourceName
, sourceLocation
);
124 String domainString
= connectorProperties
.getProperty(Constants
.SERVER_HOST
);
126 // Parse users email address
127 if (domainString
.contains(Constants
.DOMAIN_REPLACE
)) {
128 String emailAddr
= ctx
.getPrincipal().getUser().getUsername();
129 if (!emailAddr
.contains("@")) {
130 emailAddr
= ctx
.getPrincipal().getUser().getEmail();
132 String
[] emailComponenets
= emailAddr
.split("@");
133 String domain
= emailComponenets
[1];
134 domainString
= domainString
.replace(Constants
.DOMAIN_REPLACE
, domain
);
137 addrStoreProperties
.setProperty(MultipleSourceVCardObjectStore
.PROPERTY_SERVER
,
139 addrStoreProperties
.setProperty(MultipleSourceVCardObjectStore
.PROPERTY_SERVER
, connectorProperties
.getProperty(Constants
.SERVER_HOST
));
140 addrStoreProperties
.setProperty(MultipleSourceVCardObjectStore
.PROPERTY_STORE_LOCATION
, storeDir
);
141 String collectionMode
= connectorProperties
.getProperty(StoreConstants
.PROPERTY_SERVER_MODE
);
142 if (collectionMode
!= null)
143 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_SERVER_MODE
, collectionMode
);
144 String cthreads
= connectorProperties
.getProperty(StoreConstants
.PROPERTY_SERVER_CTHREADS
);
145 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_SERVER_CTHREADS
, cthreads
);
146 String citems
= connectorProperties
.getProperty(StoreConstants
.PROPERTY_SERVER_ITEMS
);
147 addrStoreProperties
.setProperty(StoreConstants
.PROPERTY_SERVER_ITEMS
, citems
);
149 so
= new MultipleSourceVCardObjectStore(stdir
.toString(), storeopts
);
150 so
.setProperties(addrStoreProperties
);
151 so
.loadSourcesFromProps();
153 so
.connect_Base64(creds
);
156 } catch (Exception e
) {
158 log
.throwing("ContactSyncSource", e
.getMessage(), e
);
159 throw new SyncSourceException(e
);
163 public SyncItemKey
[] getAllSyncItemKeys() {
164 log
.info("getAllSyncItemKeys()");
165 SyncItemKey
[] newKeys
= this.getNewSyncItemKeys(null, null);
166 SyncItemKey
[] updatedKeys
= this.getUpdatedSyncItemKeys(null, null);
167 SyncItemKey
[] allKnownKeys
= new SyncItemKey
[newKeys
.length
+ updatedKeys
.length
];
169 for (int i
= 0; i
< newKeys
.length
; i
++) {
170 allKnownKeys
[curKey
] = newKeys
[i
];
173 for (int i
= 0; i
< updatedKeys
.length
; i
++) {
174 allKnownKeys
[curKey
] = updatedKeys
[i
];
180 /** Lists all deleted items since last sync.
182 * @param sinceTs Not used
183 * @param untilTs Not used
184 * @throws com.funambol.framework.engine.source.SyncSourceException
186 public SyncItemKey
[] getDeletedSyncItemKeys(java
.sql
.Timestamp sinceTs
, java
.sql
.Timestamp untilTs
)
187 throws SyncSourceException
{
188 log
.info("getDeletedSyncItemKeys()");
189 ArrayList deletedUIDS
= so
.getDeletedFromStoreUIDS();
190 SyncItemKey
[] keys
= new SyncItemKey
[deletedUIDS
.size()];
191 for (int i
= 0; i
< deletedUIDS
.size(); i
++) {
192 String key
= (String
) deletedUIDS
.get(i
);
193 SyncItemKey sk
= new SyncItemKey(key
);
199 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
200 // #[regen=yes,id=DCE.EE4A388C-A94E-B384-EC8A-2E349EACBA40]
202 public SyncItemKey
[] getNewSyncItemKeys(java
.sql
.Timestamp sinceTs
, java
.sql
.Timestamp untilTs
) {
203 log
.info("getNewSyncItemKeys()");
204 ArrayList newUIDS
= so
.getAddedToStoreUIDS();
205 SyncItemKey
[] keys
= new SyncItemKey
[newUIDS
.size()];
206 for (int i
= 0; i
< newUIDS
.size(); i
++) {
207 String key
= (String
) newUIDS
.get(i
);
208 SyncItemKey sk
= new SyncItemKey(key
);
210 itemStates
.put(sk
, "new");
215 public SyncItem
getSyncItemFromId(SyncItemKey syncItemKey
) throws SyncSourceException
{
216 String uid
= syncItemKey
.getKeyAsString();
217 log
.info("getSyncItemFromId(" + uid
+ ")");
219 Contact object
= so
.getObjectFromStore(uid
);
220 OutboundFunambolContactObject obc
= getOutboundProcessor(object
);
221 SyncItem si
= obc
.generateSyncItem(this);
222 si
.setState(getStateForItem(syncItemKey
));
223 si
.setType(this.getType());
225 } catch (Exception ex
) {
226 log
.log(Level
.SEVERE
, "getSyncItemFromId", ex
);
227 throw new SyncSourceException(ex
);
231 public void removeSyncItem(SyncItemKey syncItem
, Timestamp arg1
, boolean arg2
) throws SyncSourceException
{
232 log
.info("removeSyncItem(" + syncItem
.getKeyAsString() + ")");
234 so
.deleteObject(syncItem
.getKeyAsString());
235 } catch (Exception e
) {
236 throw new SyncSourceException(e
);
240 public SyncItem
updateSyncItem(SyncItem toUpdate
) throws SyncSourceException
{
241 log
.info("updateSyncItem(" + toUpdate
.getKey().getKeyAsString() + ")");
243 InboundFunambolContactObject ifcb
= initInboundProcessor(toUpdate
);
244 String output
= ifcb
.getStringRepresentation(Constants
.TYPE_CONTACT_VCARD21
);
245 String name
= ifcb
.getObjectName();
246 String uid
= ifcb
.getUid();
247 int status
= so
.replaceObject(uid
, name
, output
);
248 return getSyncItemFromId(toUpdate
.getKey());
249 } catch (Exception e
) {
250 log
.info("Exception caught in updateSyncItem");
251 throw new SyncSourceException(e
);
255 public SyncItem
addSyncItem(SyncItem toAdd
) throws SyncSourceException
{
256 log
.info("addSyncItem(" + toAdd
.getKey().getKeyAsString() + ")");
258 InboundFunambolContactObject ifcb
= initInboundProcessor(toAdd
);
259 String output
= ifcb
.getStringRepresentation(Constants
.TYPE_CONTACT_VCARD21
);
260 String name
= ifcb
.getObjectName();
261 String uid
= ifcb
.getUid();
262 String srvUid
= so
.addObject(ifcb
.getDestinationStore(), uid
, name
, output
);
263 return getSyncItemFromId(new SyncItemKey(srvUid
));
264 } catch (Exception e
) {
265 log
.info("Exception caught in addSyncItem");
266 throw new SyncSourceException(e
);
270 public SyncItemKey
[] getSyncItemKeysFromTwin(SyncItem syncItem
) throws SyncSourceException
{
271 log
.info("getSyncItemKeysFromTwin " + syncItem
.getKey().getKeyAsString());
272 String uid
= syncItem
.getKey().getKeyAsString();
273 String data
= new String(syncItem
.getContent());
275 InboundFunambolContactObject ifcb
= initInboundProcessor(syncItem
);
276 String name
= ifcb
.getObjectName();
278 // No name? Lets get out of here!
281 String match
= so
.searchUids(name
);
283 log
.info("Match found: " + match
);
284 SyncItemKey
[] matches
= new SyncItemKey
[1];
285 matches
[0] = new SyncItemKey(match
);
288 } catch (Exception ex
) {
289 log
.info("Error cause data=" + data
);
290 log
.log(Level
.SEVERE
, "getSyncItemKeysFromTwin", ex
);
291 throw new SyncSourceException(ex
);
296 public void setOperationStatus(String reason
, int status
, SyncItemKey
[] keys
) {
297 for (int i
= 0; i
< keys
.length
; i
++) {
298 SyncItemKey syncItemKey
= keys
[i
];
299 log
.info("setOperationStatus(" + reason
+ "," + status
+ "," + syncItemKey
.getKeyAsString() + ")");
305 public void endSync() throws SyncSourceException
{
306 log
.info("endSync()");
310 } catch (Exception e
) {
311 log
.info("Exception caught in endSync()");
312 throw new SyncSourceException(e
);
318 * Called to get the keys of the items updated in the time frame sinceTs - untilTs.
319 * <br><code>sinceTs</code> null means all keys of the items updated until <code>untilTs</code>.
320 * <br><code>untilTs</code> null means all keys of the items updated since <code>sinceTs</code>.
321 * @param sinceTs consider the changes since this point in time.
322 * @param untilTs consider the changes until this point in time.
323 * @return an array of keys containing the <code>SyncItemKey</code>'s key of the updated
324 * items in the given time frame. It MUST NOT return null for
325 * no keys, but instad an empty array.
327 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
328 // #[regen=yes,id=DCE.A80FDB1D-9EC6-0537-6211-5EC359BAAB6D]
330 public SyncItemKey
[] getUpdatedSyncItemKeys(Timestamp sinceTs
, Timestamp untilTs
) {
331 log
.info("getUpdatedSyncItemKeys()");
332 ArrayList updatedKeys
= so
.getUpdatedInStoreUIDS();
333 SyncItemKey
[] keys
= new SyncItemKey
[updatedKeys
.size()];
334 for (int i
= 0; i
< updatedKeys
.size(); i
++) {
335 String key
= (String
) updatedKeys
.get(i
);
336 SyncItemKey sk
= new SyncItemKey(key
);
338 itemStates
.put(sk
, "updated");
343 public void init() throws BeanInitializationException
{
346 private OutboundFunambolContactObject
getOutboundProcessor(Contact origObject
) throws Exception
{
347 OutboundFunambolContactObject obc
= new OutboundFunambolContactObject();
348 obc
.setContactObject(origObject
);
352 private char getStateForItem(SyncItemKey sik
) {
353 String state
= (String
) itemStates
.get(sik
);
355 return SyncItemState
.SYNCHRONIZED
;
356 } else if (state
.equals("new")) {
357 return SyncItemState
.NEW
;
358 } else if (state
.equals("updated")) {
359 return SyncItemState
.UPDATED
;
361 return SyncItemState
.UNKNOWN
;
364 public void setConnectorProperties(Properties p
) {
365 connectorProperties
= p
;
368 public Properties
getConnectorProperties() {
369 return connectorProperties
;
372 public SyncContext
getSyncContext() {
376 /** Create the inbound object processing class (default is
377 * InboundFunambolContactObject
378 * @see InboundFunambolContactObject
380 private InboundFunambolContactObject
initInboundProcessor(SyncItem si
) throws Exception
{
381 InboundFunambolContactObject ifcb
;
382 ifcb
= new InboundFunambolContactObject();
383 ifcb
.setSyncSource(this);
384 ifcb
.setOriginalSyncItem(si
);