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
.utils
.HTMLFormatter
;
54 public class ContactSyncSource
extends AbstractSyncSource
implements LazyInitBean
, Serializable
{
56 private Properties connectorProperties
= null;
57 private MultipleSourceVCardObjectStore so
= null;
58 private FileHandler fh
= null;
59 private Logger log
= null;
60 private int syncType
= 0;
61 private Map itemStates
= null;
62 private Map sources
= null;
63 private Properties addrStoreProperties
= null;
64 private SyncContext ctx
;
66 public ContactSyncSource() {
69 public void beginSync(SyncContext syncContext
) throws SyncSourceException
{
70 log
= Logger
.getLogger(Constants
.CONTACT_LOGGER_NAME
+"-"+System
.currentTimeMillis());
71 log
.info("Begin sync for Contact Sync Source");
72 this.ctx
= syncContext
;
73 String principalName
= Utilities
.cleanFilePath(ctx
.getPrincipal().getName());
74 String storeDir
= connectorProperties
.getProperty(Constants
.STOREDIR_PATH
);
75 File stdir
= new File(storeDir
+ File
.separatorChar
+ principalName
+ File
.separatorChar
);
79 if (!stdir
.exists()) {
83 HTMLFormatter hf
= new HTMLFormatter();
84 if (connectorProperties
.getProperty(Constants
.SINGLE_LOG
) != null) {
85 fh
= new FileHandler(stdir
.toString() + File
.separatorChar
+ "connector.html");
86 storeopts
+= MultipleSourceVCardObjectStore
.OPTION_SINGLELOG
;
88 long curTime
= new java
.util
.Date().getTime();
89 fh
= new FileHandler(stdir
.toString() + File
.separatorChar
+ "connector-" + curTime
+ ".html");
93 log
.setLevel(Level
.ALL
);
95 itemStates
= new Hashtable();
96 syncType
= ctx
.getSyncMode();
97 log
.info("Beginning sync for " + principalName
+ " for mode=" + syncType
);
98 if (syncType
== 201 || syncType
== 205) {
101 } catch (Exception e
) {
103 log
.throwing("beginSync", "ContactSyncSource", e
);
104 throw new SyncSourceException(e
);
107 itemStates
= new Hashtable();
108 syncType
= ctx
.getSyncMode();
109 String creds
= ctx
.getPrincipal().getEncodedCredentials();
110 sources
= new Hashtable();
111 addrStoreProperties
= new Properties();
112 // Process the sources
113 Iterator configKeys
= connectorProperties
.keySet().iterator();
114 while (configKeys
.hasNext()) {
115 String keyName
= (String
) configKeys
.next();
116 if (keyName
.contains(Constants
.SOURCE_LOCATION_BASE
)) {
117 String sourceLocation
= connectorProperties
.getProperty(keyName
);
118 sourceLocation
= sourceLocation
.replace("%USER%", syncContext
.getPrincipal().getUsername());
119 String sourceName
= MultipleSourceVCardObjectStore
.BASE_PROPERTY_SOURCE
.concat(keyName
.replace(Constants
.SOURCE_LOCATION_BASE
, ""));
120 addrStoreProperties
.setProperty(sourceName
, sourceLocation
);
123 String domainString
= connectorProperties
.getProperty(Constants
.SERVER_HOST
);
125 // Parse users email address
126 if (domainString
.contains(Constants
.DOMAIN_REPLACE
)) {
127 String emailAddr
= ctx
.getPrincipal().getUser().getUsername();
128 if (!emailAddr
.contains("@")) {
129 emailAddr
= ctx
.getPrincipal().getUser().getEmail();
131 String
[] emailComponenets
= emailAddr
.split("@");
132 String domain
= emailComponenets
[1];
133 domainString
= domainString
.replace(Constants
.DOMAIN_REPLACE
, domain
);
136 addrStoreProperties
.setProperty(MultipleSourceVCardObjectStore
.PROPERTY_SERVER
,
138 addrStoreProperties
.setProperty(MultipleSourceVCardObjectStore
.PROPERTY_SERVER
, connectorProperties
.getProperty(Constants
.SERVER_HOST
));
139 addrStoreProperties
.setProperty(MultipleSourceVCardObjectStore
.PROPERTY_STORE_LOCATION
, storeDir
);
141 so
= new MultipleSourceVCardObjectStore(stdir
.toString(), storeopts
);
142 so
.setProperties(addrStoreProperties
);
143 so
.loadSourcesFromProps();
145 so
.connect_Base64(creds
);
148 } catch (Exception e
) {
150 log
.throwing("ContactSyncSource", e
.getMessage(), e
);
151 throw new SyncSourceException(e
);
155 public SyncItemKey
[] getAllSyncItemKeys() {
156 log
.info("getAllSyncItemKeys()");
157 SyncItemKey
[] newKeys
= this.getNewSyncItemKeys(null, null);
158 SyncItemKey
[] updatedKeys
= this.getUpdatedSyncItemKeys(null, null);
159 SyncItemKey
[] allKnownKeys
= new SyncItemKey
[newKeys
.length
+ updatedKeys
.length
];
161 for (int i
= 0; i
< newKeys
.length
; i
++) {
162 allKnownKeys
[curKey
] = newKeys
[i
];
165 for (int i
= 0; i
< updatedKeys
.length
; i
++) {
166 allKnownKeys
[curKey
] = updatedKeys
[i
];
172 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
173 // #[regen=yes,id=DCE.E775E834-2208-1C54-5D7B-F71213CEDF17]
175 public SyncItemKey
[] getDeletedSyncItemKeys(java
.sql
.Timestamp sinceTs
, java
.sql
.Timestamp untilTs
)
176 throws SyncSourceException
{
177 log
.info("getDeletedSyncItemKeys()");
178 ArrayList deletedUIDS
= so
.getDeletedFromStoreUIDS();
179 SyncItemKey
[] keys
= new SyncItemKey
[deletedUIDS
.size()];
180 for (int i
= 0; i
< deletedUIDS
.size(); i
++) {
181 String key
= (String
) deletedUIDS
.get(i
);
182 SyncItemKey sk
= new SyncItemKey(key
);
188 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
189 // #[regen=yes,id=DCE.EE4A388C-A94E-B384-EC8A-2E349EACBA40]
191 public SyncItemKey
[] getNewSyncItemKeys(java
.sql
.Timestamp sinceTs
, java
.sql
.Timestamp untilTs
) {
192 log
.info("getNewSyncItemKeys()");
193 ArrayList newUIDS
= so
.getAddedToStoreUIDS();
194 SyncItemKey
[] keys
= new SyncItemKey
[newUIDS
.size()];
195 for (int i
= 0; i
< newUIDS
.size(); i
++) {
196 String key
= (String
) newUIDS
.get(i
);
197 SyncItemKey sk
= new SyncItemKey(key
);
199 itemStates
.put(sk
, "new");
204 public SyncItem
getSyncItemFromId(SyncItemKey syncItemKey
) throws SyncSourceException
{
205 String uid
= syncItemKey
.getKeyAsString();
206 log
.info("getSyncItemFromId(" + uid
+ ")");
208 Contact object
= so
.getObjectFromStore(uid
);
209 OutboundFunambolContactObject obc
= getOutboundProcessor(object
);
210 SyncItem si
= obc
.generateSyncItem(this);
211 si
.setState(getStateForItem(syncItemKey
));
212 si
.setType(this.getType());
214 } catch (Exception ex
) {
215 log
.log(Level
.SEVERE
, "getSyncItemFromId", ex
);
216 throw new SyncSourceException(ex
);
220 public void removeSyncItem(SyncItemKey syncItem
, Timestamp arg1
, boolean arg2
) throws SyncSourceException
{
221 log
.info("removeSyncItem(" + syncItem
.getKeyAsString() + ")");
223 so
.deleteObject(syncItem
.getKeyAsString());
224 } catch (Exception e
) {
225 throw new SyncSourceException(e
);
229 public SyncItem
updateSyncItem(SyncItem toUpdate
) throws SyncSourceException
{
230 log
.info("updateSyncItem(" + toUpdate
.getKey().getKeyAsString() + ")");
232 InboundFunambolContactObject ifcb
= initInboundProcessor(toUpdate
);
233 String output
= ifcb
.getStringRepresentation(Constants
.TYPE_CONTACT_VCARD21
);
234 String name
= ifcb
.getObjectName();
235 String uid
= ifcb
.getUid();
236 so
.replaceObject(uid
, name
, output
);
237 return getSyncItemFromId(toUpdate
.getKey());
238 } catch (Exception e
) {
239 log
.info("Exception caught in updateSyncItem");
240 throw new SyncSourceException(e
);
244 public SyncItem
addSyncItem(SyncItem toAdd
) throws SyncSourceException
{
245 log
.info("addSyncItem(" + toAdd
.getKey().getKeyAsString() + ")");
247 InboundFunambolContactObject ifcb
= initInboundProcessor(toAdd
);
248 String output
= ifcb
.getStringRepresentation(Constants
.TYPE_CONTACT_VCARD21
);
249 String name
= ifcb
.getObjectName();
250 String uid
= ifcb
.getUid();
251 String srvUid
= so
.addObject(ifcb
.getDestinationStore(), uid
, name
, output
);
252 return getSyncItemFromId(new SyncItemKey(srvUid
));
253 } catch (Exception e
) {
254 log
.info("Exception caught in addSyncItem");
255 throw new SyncSourceException(e
);
259 public SyncItemKey
[] getSyncItemKeysFromTwin(SyncItem syncItem
) throws SyncSourceException
{
260 log
.info("getSyncItemKeysFromTwin " + syncItem
.getKey().getKeyAsString());
261 String uid
= syncItem
.getKey().getKeyAsString();
262 String data
= new String(syncItem
.getContent());
264 InboundFunambolContactObject ifcb
= initInboundProcessor(syncItem
);
265 String name
= ifcb
.getObjectName();
266 String match
= so
.searchUids(name
);
268 log
.info("Match found: " + match
);
269 SyncItemKey
[] matches
= new SyncItemKey
[1];
270 matches
[0] = new SyncItemKey(match
);
273 } catch (Exception ex
) {
274 log
.info("Error cause data=" + data
);
275 log
.log(Level
.SEVERE
, "getSyncItemKeysFromTwin", ex
);
276 throw new SyncSourceException(ex
);
281 public void setOperationStatus(String reason
, int status
, SyncItemKey
[] keys
) {
282 for (int i
= 0; i
< keys
.length
; i
++) {
283 SyncItemKey syncItemKey
= keys
[i
];
284 log
.info("setOperationStatus(" + reason
+ "," + status
+ "," + syncItemKey
.getKeyAsString() + ")");
290 public void endSync() throws SyncSourceException
{
291 log
.info("endSync()");
295 } catch (Exception e
) {
296 log
.info("Exception caught in endSync()");
297 throw new SyncSourceException(e
);
303 * Called to get the keys of the items updated in the time frame sinceTs - untilTs.
304 * <br><code>sinceTs</code> null means all keys of the items updated until <code>untilTs</code>.
305 * <br><code>untilTs</code> null means all keys of the items updated since <code>sinceTs</code>.
306 * @param sinceTs consider the changes since this point in time.
307 * @param untilTs consider the changes until this point in time.
308 * @return an array of keys containing the <code>SyncItemKey</code>'s key of the updated
309 * items in the given time frame. It MUST NOT return null for
310 * no keys, but instad an empty array.
312 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
313 // #[regen=yes,id=DCE.A80FDB1D-9EC6-0537-6211-5EC359BAAB6D]
315 public SyncItemKey
[] getUpdatedSyncItemKeys(Timestamp sinceTs
, Timestamp untilTs
) {
316 log
.info("getUpdatedSyncItemKeys()");
317 ArrayList updatedKeys
= so
.getUpdatedInStoreUIDS();
318 SyncItemKey
[] keys
= new SyncItemKey
[updatedKeys
.size()];
319 for (int i
= 0; i
< updatedKeys
.size(); i
++) {
320 String key
= (String
) updatedKeys
.get(i
);
321 SyncItemKey sk
= new SyncItemKey(key
);
323 itemStates
.put(sk
, "updated");
328 public void init() throws BeanInitializationException
{
331 private OutboundFunambolContactObject
getOutboundProcessor(Contact origObject
) throws Exception
{
332 OutboundFunambolContactObject obc
= new OutboundFunambolContactObject();
333 obc
.setContactObject(origObject
);
337 private char getStateForItem(SyncItemKey sik
) {
338 String state
= (String
) itemStates
.get(sik
);
340 return SyncItemState
.SYNCHRONIZED
;
341 } else if (state
.equals("new")) {
342 return SyncItemState
.NEW
;
343 } else if (state
.equals("updated")) {
344 return SyncItemState
.UPDATED
;
346 return SyncItemState
.UNKNOWN
;
349 public void setConnectorProperties(Properties p
) {
350 connectorProperties
= p
;
353 public Properties
getConnectorProperties() {
354 return connectorProperties
;
357 public SyncContext
getSyncContext() {
361 /** Create the inbound object processing class (default is
362 * InboundFunambolContactObject
363 * @see InboundFunambolContactObject
365 private InboundFunambolContactObject
initInboundProcessor(SyncItem si
) throws Exception
{
366 InboundFunambolContactObject ifcb
;
367 ifcb
= new InboundFunambolContactObject();
368 ifcb
.setSyncSource(this);
369 ifcb
.setOriginalSyncItem(si
);