package com.rapidftr.services; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import org.json.me.JSONArray; import org.json.me.JSONObject; import com.rapidftr.datastore.ChildAction; import com.rapidftr.datastore.Children; import com.rapidftr.datastore.ChildrenRecordStore; import com.rapidftr.model.Child; import com.rapidftr.net.HttpServer; import com.rapidftr.net.HttpService; import com.rapidftr.utilities.DateFormatter; import com.rapidftr.utilities.HttpUtility; import com.sun.me.web.path.Result; import com.sun.me.web.request.Arg; import com.sun.me.web.request.PostData; import com.sun.me.web.request.Response; public class ChildSyncService extends RequestAwareService { public static final String PROCESS_STATE = "process_state"; public static final String CHILD_TO_SYNC = "childToSync"; private final ChildrenRecordStore childRecordStore; private ChildPhotoUpdater childPhotoUpdater; private HttpService httpService; private final DateFormatter dateFormatter; public ChildSyncService(HttpService httpService, ChildrenRecordStore childRecordStore, ChildPhotoUpdater photoUpdater, DateFormatter dateFormatter) { // TODO get rid of dependency on RequestAwareService/requesthandler super(httpService); this.childRecordStore = childRecordStore; this.childPhotoUpdater = photoUpdater; this.httpService = httpService; this.dateFormatter = dateFormatter; } public ChildSyncService(HttpService httpService, ChildrenRecordStore childRecordStore, DateFormatter dateFormatter) { this(httpService, childRecordStore, new ChildPhotoUpdater(httpService, childRecordStore), dateFormatter); } private void uploadChildren(final Children children, final ChildSyncListener listener) { children.forEachChild(new ChildAction() { int index = 0; public void execute(Child child) { Hashtable context = new Hashtable(); PostData postData = child.getPostData(); context.put(PROCESS_STATE, "Uploading [" + (++index) + "/" + children.count() + "]"); context.put(CHILD_TO_SYNC, child); Arg multiPart = new Arg("Content-Type", "multipart/form-data;boundary=" + postData.getBoundary()); Arg json = HttpUtility.HEADER_ACCEPT_JSON; Arg[] httpArgs = { multiPart, json }; if (child.isNewChild()) { httpService.post("children", null, httpArgs, listener, postData, context); } else if (child.isUpdated() || child.isSyncFailed()) { httpService.put("children/" + child.getField("_id"), null, httpArgs, listener, postData, context); } } }); } public void syncChildRecord(Child child) { Vector childrenList = new Vector(); childrenList.addElement(child); ChildSyncListener listener = new ChildSyncListener(requestHandler.getRequestCallBack(), 1, childRecordStore, childPhotoUpdater); uploadChildren(new Children(childrenList), listener); } public void syncAllChildRecords() throws ServiceException { new Thread() { public void run() { requestHandler.getRequestCallBack().updateProgressMessage( "Syncing"); Children childrenToUpload = childrenToBeUploaded(); try { final Vector childrenToBeDownloaded = childRecordsNeedToBeDownload(); ChildSyncListener listener = new ChildSyncListener(requestHandler.getRequestCallBack(), childrenToUpload.count() + childrenToBeDownloaded.size(), childRecordStore, childPhotoUpdater); uploadChildren(childrenToUpload, listener); downloadNewChildRecords(childrenToBeDownloaded, listener); } catch (Exception e) { e.printStackTrace(); requestHandler.markProcessFailed("Error syncing children"); } }; }.start(); } private Children childrenToBeUploaded() { return childRecordStore.getAll().getChildrenToUpload(); } private void downloadNewChildRecords(Vector childrenToBeDownloaded, final ChildSyncListener listener) { Enumeration items = childrenToBeDownloaded.elements(); int index = 0; while (items.hasMoreElements()) { index++; Hashtable context = new Hashtable(); context.put(PROCESS_STATE, "Downloading [" + index + "/" + childrenToBeDownloaded.size() + "]"); Child child = new Child(dateFormatter.getCurrentFormattedDateTime()); String childId = items.nextElement().toString(); child.setField("_id", childId); child.setField("name", childId); child.setField("last_known_location", "NA"); context.put(CHILD_TO_SYNC, child); httpService.get("children/" + childId, null, HttpUtility .makeJSONHeader(), listener, context); } } private Vector childRecordsNeedToBeDownload() throws Exception { Vector childNeedToDownload = new Vector(); Hashtable offlineIdRevXREF = getOfflineStoredChildrenIdRevMapping(); Hashtable onlineIdRevXREF = getOnlineStoredChildrenIdRevMapping(); Enumeration items = onlineIdRevXREF.keys(); while (items.hasMoreElements()) { String key = (String) items.nextElement(); if ((!offlineIdRevXREF.containsKey(key)) || (offlineIdRevXREF.containsKey(key) && !offlineIdRevXREF .get(key).equals(onlineIdRevXREF.get(key)))) { childNeedToDownload.addElement(key); } } return childNeedToDownload; } private Hashtable getOnlineStoredChildrenIdRevMapping() throws Exception { Hashtable mapping = new Hashtable(); Response response; response = requestHandler.get("children-ids", null, HttpUtility .makeJSONHeader()); if (response != null) { Result result = response.getResult(); HttpServer.printResponse(response); JSONArray jsonChildren; jsonChildren = result.getAsArray(""); for (int i = 0; i < jsonChildren.length(); i++) { JSONObject jsonChild = (JSONObject) jsonChildren.get(i); mapping.put(jsonChild.getString("id"), jsonChild .getString("rev")); } } return mapping; } private Hashtable getOfflineStoredChildrenIdRevMapping() { final Hashtable mapping = new Hashtable(); childRecordStore.getAll().forEachChild(new ChildAction() { public void execute(Child child) { String id = child.getField("_id"); String rev = child.getField("_rev"); if (id != null && rev != null) { mapping.put(id, rev); } } }); return mapping; } public void clearState() { childRecordStore.deleteAll(); } // TODO this is not used anymore - to remove this need to get rid of dependency on RequestAwareService public void onRequestFailure(Object context, Exception exception) { } public void onRequestSuccess(Object context, Response response) { } }