Catch throwables, not just exceptions (catches contact parser errors)
[funambol-groupdav-connector.git] / src / main / java / net / bionicmessage / funambol / groupdav / contacts / ContactSyncSource.java
blob748f7e1863459b5d733eddb8868866a4b140862f
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.net.URI;
37 import java.sql.Timestamp;
38 import java.util.ArrayList;
39 import java.util.Hashtable;
40 import java.util.Iterator;
41 import java.util.Map;
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;
52 /**
54 * @author matt
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);
78 int storeopts = 0;
79 try {
80 /** Set up logging */
81 if (!stdir.exists()) {
82 stdir.mkdirs();
84 // Setup the logger
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;
89 } else {
90 long curTime = new java.util.Date().getTime();
91 fh = new FileHandler(stdir.toString() + File.separatorChar + "connector-" + curTime + ".html");
93 fh.setFormatter(hf);
94 log.addHandler(fh);
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) {
101 storeopts += 64;
103 } catch (Exception e) {
104 e.printStackTrace();
105 log.throwing("beginSync", "ContactSyncSource", e);
106 throw new SyncSourceException(e);
108 // Perform setup
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,
138 domainString);
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);
147 // Create store
148 so = new MultipleSourceVCardObjectStore(stdir.toString(), storeopts);
149 so.setProperties(addrStoreProperties);
150 so.loadSourcesFromProps();
151 try {
152 so.setServer(new URI(domainString), creds);
153 // Sync
154 so.startSync();
155 } catch (Exception e) {
156 e.printStackTrace();
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];
167 int curKey = 0;
168 for (int i = 0; i < newKeys.length; i++) {
169 allKnownKeys[curKey] = newKeys[i];
170 curKey++;
172 for (int i = 0; i < updatedKeys.length; i++) {
173 allKnownKeys[curKey] = updatedKeys[i];
174 curKey++;
176 return allKnownKeys;
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);
193 keys[i] = sk;
195 return keys;
198 // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
199 // #[regen=yes,id=DCE.EE4A388C-A94E-B384-EC8A-2E349EACBA40]
200 // </editor-fold>
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);
208 keys[i] = sk;
209 itemStates.put(sk, "new");
211 return keys;
214 public SyncItem getSyncItemFromId(SyncItemKey syncItemKey) throws SyncSourceException {
215 String uid = syncItemKey.getKeyAsString();
216 log.info("getSyncItemFromId(" + uid + ")");
217 try {
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());
223 return si;
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() + ")");
232 try {
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() + ")");
241 try {
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() + ")");
256 try {
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());
273 try {
274 InboundFunambolContactObject ifcb = initInboundProcessor(syncItem);
275 String name = ifcb.getObjectName();
276 if (name == null) {
277 // No name? Lets get out of here!
278 return null;
280 String match = so.searchUids(name, 0,0);
281 if (match != null) {
282 log.info("Match found: " + match);
283 SyncItemKey[] matches = new SyncItemKey[1];
284 matches[0] = new SyncItemKey(match);
285 return matches;
287 } catch (Exception ex) {
288 log.info("Error cause data=" + data);
289 log.log(Level.SEVERE, "getSyncItemKeysFromTwin", ex);
290 throw new SyncSourceException(ex);
292 return null;
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() + ")");
303 @Override
304 public void endSync() throws SyncSourceException {
305 log.info("endSync()");
306 try {
307 so.close();
308 fh.close();
309 } catch (Exception e) {
310 log.info("Exception caught in endSync()");
311 throw new SyncSourceException(e);
313 super.endSync();
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]
328 // </editor-fold>
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);
336 keys[i] = sk;
337 itemStates.put(sk, "updated");
339 return keys;
342 public void init() throws BeanInitializationException {
345 private OutboundFunambolContactObject getOutboundProcessor(Contact origObject) throws Exception {
346 OutboundFunambolContactObject obc = new OutboundFunambolContactObject();
347 obc.setContactObject(origObject);
348 return obc;
351 private char getStateForItem(SyncItemKey sik) {
352 String state = (String) itemStates.get(sik);
353 if (state == null) {
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() {
372 return ctx;
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);
384 return ifcb;