upgrade to ical4j beta4, ContactSyncSource will not attempt to find existing items...
[funambol-groupdav-connector.git] / src / main / java / net / bionicmessage / funambol / groupdav / contacts / ContactSyncSource.java
blobdbe85179f71cf0b971a73468d8036ab80d4ce883
1 /*
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;
34 import java.io.File;
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;
40 import java.util.Map;
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;
51 /**
53 * @author matt
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);
77 int storeopts = 0;
78 try {
79 /** Set up logging */
80 if (!stdir.exists()) {
81 stdir.mkdirs();
83 // Setup the logger
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;
88 } else {
89 long curTime = new java.util.Date().getTime();
90 fh = new FileHandler(stdir.toString() + File.separatorChar + "connector-" + curTime + ".html");
92 fh.setFormatter(hf);
93 log.addHandler(fh);
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) {
100 storeopts += 64;
102 } catch (Exception e) {
103 e.printStackTrace();
104 log.throwing("beginSync", "ContactSyncSource", e);
105 throw new SyncSourceException(e);
107 // Perform setup
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);
135 int one = 2;
137 addrStoreProperties.setProperty(MultipleSourceVCardObjectStore.PROPERTY_SERVER,
138 domainString);
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);
148 // Create store
149 so = new MultipleSourceVCardObjectStore(stdir.toString(), storeopts);
150 so.setProperties(addrStoreProperties);
151 so.loadSourcesFromProps();
152 try {
153 so.connect_Base64(creds);
154 // Sync
155 so.startSync();
156 } catch (Exception e) {
157 e.printStackTrace();
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];
168 int curKey = 0;
169 for (int i = 0; i < newKeys.length; i++) {
170 allKnownKeys[curKey] = newKeys[i];
171 curKey++;
173 for (int i = 0; i < updatedKeys.length; i++) {
174 allKnownKeys[curKey] = updatedKeys[i];
175 curKey++;
177 return allKnownKeys;
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);
194 keys[i] = sk;
196 return keys;
199 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
200 // #[regen=yes,id=DCE.EE4A388C-A94E-B384-EC8A-2E349EACBA40]
201 // </editor-fold>
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);
209 keys[i] = sk;
210 itemStates.put(sk, "new");
212 return keys;
215 public SyncItem getSyncItemFromId(SyncItemKey syncItemKey) throws SyncSourceException {
216 String uid = syncItemKey.getKeyAsString();
217 log.info("getSyncItemFromId(" + uid + ")");
218 try {
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());
224 return si;
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() + ")");
233 try {
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() + ")");
242 try {
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() + ")");
257 try {
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());
274 try {
275 InboundFunambolContactObject ifcb = initInboundProcessor(syncItem);
276 String name = ifcb.getObjectName();
277 if (name == null) {
278 // No name? Lets get out of here!
279 return null;
281 String match = so.searchUids(name);
282 if (match != null) {
283 log.info("Match found: " + match);
284 SyncItemKey[] matches = new SyncItemKey[1];
285 matches[0] = new SyncItemKey(match);
286 return matches;
288 } catch (Exception ex) {
289 log.info("Error cause data=" + data);
290 log.log(Level.SEVERE, "getSyncItemKeysFromTwin", ex);
291 throw new SyncSourceException(ex);
293 return null;
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() + ")");
304 @Override
305 public void endSync() throws SyncSourceException {
306 log.info("endSync()");
307 try {
308 so.close();
309 fh.close();
310 } catch (Exception e) {
311 log.info("Exception caught in endSync()");
312 throw new SyncSourceException(e);
314 super.endSync();
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]
329 // </editor-fold>
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);
337 keys[i] = sk;
338 itemStates.put(sk, "updated");
340 return keys;
343 public void init() throws BeanInitializationException {
346 private OutboundFunambolContactObject getOutboundProcessor(Contact origObject) throws Exception {
347 OutboundFunambolContactObject obc = new OutboundFunambolContactObject();
348 obc.setContactObject(origObject);
349 return obc;
352 private char getStateForItem(SyncItemKey sik) {
353 String state = (String) itemStates.get(sik);
354 if (state == null) {
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() {
373 return ctx;
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);
385 return ifcb;