KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > entityext > synchronization > EntitySyncServices


1 /*
2  * $Id: EntitySyncServices.java 5462 2005-08-05 18:35:48Z jonesde $
3  *
4  * Copyright (c) 2001-2005 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  */

25 package org.ofbiz.entityext.synchronization;
26
27 import java.sql.Timestamp JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Calendar JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Date JavaDoc;
37 import java.text.SimpleDateFormat JavaDoc;
38 import java.io.IOException JavaDoc;
39 import java.net.URL JavaDoc;
40
41 import javax.xml.parsers.ParserConfigurationException JavaDoc;
42
43 import org.ofbiz.base.util.Debug;
44 import org.ofbiz.base.util.UtilMisc;
45 import org.ofbiz.base.util.UtilValidate;
46 import org.ofbiz.base.util.UtilXml;
47 import org.ofbiz.base.util.UtilURL;
48 import org.ofbiz.entity.GenericDelegator;
49 import org.ofbiz.entity.GenericEntity;
50 import org.ofbiz.entity.GenericEntityException;
51 import org.ofbiz.entity.GenericValue;
52 import org.ofbiz.entity.serialize.XmlSerializer;
53 import org.ofbiz.entity.serialize.SerializeException;
54 import org.ofbiz.entity.condition.EntityExpr;
55 import org.ofbiz.entity.condition.EntityOperator;
56 import org.ofbiz.entity.model.ModelEntity;
57 import org.ofbiz.entityext.synchronization.EntitySyncContext.SyncAbortException;
58 import org.ofbiz.entityext.synchronization.EntitySyncContext.SyncErrorException;
59 import org.ofbiz.service.DispatchContext;
60 import org.ofbiz.service.GenericServiceException;
61 import org.ofbiz.service.LocalDispatcher;
62 import org.ofbiz.service.ServiceUtil;
63
64 import org.w3c.dom.Document JavaDoc;
65 import org.w3c.dom.Element JavaDoc;
66 import org.xml.sax.SAXException JavaDoc;
67
68 /**
69  * Entity Engine Sync Services
70  *
71  * @author <a HREF="mailto:jonesde@ofbiz.org">David E. Jones</a>
72  * @author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
73  * @version $Rev: 5462 $
74  * @since 3.0
75  */

76 public class EntitySyncServices {
77     
78     public static final String JavaDoc module = EntitySyncServices.class.getName();
79     
80     /**
81      * Run an Entity Sync (checks to see if other already running, etc)
82      *@param dctx The DispatchContext that this service is operating in
83      *@param context Map containing the input parameters
84      *@return Map with the result of the service, the output parameters
85      */

86     public static Map JavaDoc runEntitySync(DispatchContext dctx, Map JavaDoc context) {
87         EntitySyncContext esc = null;
88         try {
89             esc = new EntitySyncContext(dctx, context);
90             if ("Y".equals(esc.entitySync.get("forPullOnly"))) {
91                 return ServiceUtil.returnError("Cannot do Entity Sync Push because entitySyncId [] is set for Pull Only.");
92             }
93
94             esc.runPushStartRunning();
95
96             // increment starting time to run until now
97
esc.setSplitStartTime(); // just run this the first time, will be updated between each loop automatically
98
while (esc.hasMoreTimeToSync()) {
99                 
100                 // TODO make sure the following message is commented out before commit:
101
// Debug.logInfo("Doing runEntitySync split, currentRunStartTime=" + esc.currentRunStartTime + ", currentRunEndTime=" + esc.currentRunEndTime, module);
102

103                 esc.totalSplits++;
104                 
105                 // tx times are indexed
106
// keep track of how long these sync runs take and store that info on the history table
107
// saves info about removed, all entities that don't have no-auto-stamp set, this will be done in the GenericDAO like the stamp sets
108

109                 // ===== INSERTS =====
110
ArrayList JavaDoc valuesToCreate = esc.assembleValuesToCreate();
111                 // ===== UPDATES =====
112
ArrayList JavaDoc valuesToStore = esc.assembleValuesToStore();
113                 // ===== DELETES =====
114
List JavaDoc keysToRemove = esc.assembleKeysToRemove();
115                 
116                 esc.runPushSendData(valuesToCreate, valuesToStore, keysToRemove);
117                 
118                 esc.saveResultsReportedFromDataStore();
119                 esc.advanceRunTimes();
120             }
121
122             esc.saveFinalSyncResults();
123             
124         } catch (SyncAbortException e) {
125             return e.returnError(module);
126         } catch (SyncErrorException e) {
127             e.saveSyncErrorInfo(esc);
128             return e.returnError(module);
129         }
130         
131         return ServiceUtil.returnSuccess();
132     }
133     
134     /**
135      * Store Entity Sync Data
136      *@param dctx The DispatchContext that this service is operating in
137      *@param context Map containing the input parameters
138      *@return Map with the result of the service, the output parameters
139      */

140     public static Map JavaDoc storeEntitySyncData(DispatchContext dctx, Map JavaDoc context) {
141         GenericDelegator delegator = dctx.getDelegator();
142         String JavaDoc overrideDelegatorName = (String JavaDoc) context.get("delegatorName");
143         if (UtilValidate.isNotEmpty(overrideDelegatorName)) {
144             delegator = GenericDelegator.getGenericDelegator(overrideDelegatorName);
145             if (delegator == null) {
146                 return ServiceUtil.returnError("Could not find delegator with specified name " + overrideDelegatorName);
147             }
148         }
149         //LocalDispatcher dispatcher = dctx.getDispatcher();
150

151         String JavaDoc entitySyncId = (String JavaDoc) context.get("entitySyncId");
152         // incoming lists will already be sorted by lastUpdatedStamp (or lastCreatedStamp)
153
List JavaDoc valuesToCreate = (List JavaDoc) context.get("valuesToCreate");
154         List JavaDoc valuesToStore = (List JavaDoc) context.get("valuesToStore");
155         List JavaDoc keysToRemove = (List JavaDoc) context.get("keysToRemove");
156
157         if (Debug.infoOn()) Debug.logInfo("Running storeEntitySyncData (" + entitySyncId + ") - [" + valuesToCreate.size() + "] to create; [" + valuesToStore.size() + "] to store; [" + keysToRemove.size() + "] to remove.", module);
158         try {
159             long toCreateInserted = 0;
160             long toCreateUpdated = 0;
161             long toCreateNotUpdated = 0;
162             long toStoreInserted = 0;
163             long toStoreUpdated = 0;
164             long toStoreNotUpdated = 0;
165             long toRemoveDeleted = 0;
166             long toRemoveAlreadyDeleted = 0;
167             
168             // create all values in the valuesToCreate List; if the value already exists update it, or if exists and was updated more recently than this one dont update it
169
Iterator JavaDoc valueToCreateIter = valuesToCreate.iterator();
170             while (valueToCreateIter.hasNext()) {
171                 GenericValue valueToCreate = (GenericValue) valueToCreateIter.next();
172                 // to Create check if exists (find by pk), if not insert; if exists check lastUpdatedStamp: if null or before the candidate value insert, otherwise don't insert
173
// NOTE: use the delegator from this DispatchContext rather than the one named in the GenericValue
174

175                 // maintain the original timestamps when doing storage of synced data, by default with will update the timestamps to now
176
valueToCreate.setIsFromEntitySync(true);
177
178                 // check to make sure all foreign keys are created; if not create dummy values as place holders
179
valueToCreate.checkFks(true);
180
181                 GenericValue existingValue = delegator.findByPrimaryKey(valueToCreate.getPrimaryKey());
182                 if (existingValue == null) {
183                     delegator.create(valueToCreate);
184                     toCreateInserted++;
185                 } else {
186                     // if the existing value has a stamp field that is AFTER the stamp on the valueToCreate, don't update it
187
if (existingValue.get(ModelEntity.STAMP_FIELD) != null && existingValue.getTimestamp(ModelEntity.STAMP_FIELD).after(valueToCreate.getTimestamp(ModelEntity.STAMP_FIELD))) {
188                         toCreateNotUpdated++;
189                     } else {
190                         delegator.store(valueToCreate);
191                         toCreateUpdated++;
192                     }
193                 }
194             }
195             
196             // iterate through to store list and store each
197
Iterator JavaDoc valueToStoreIter = valuesToStore.iterator();
198             while (valueToStoreIter.hasNext()) {
199                 GenericValue valueToStore = (GenericValue) valueToStoreIter.next();
200                 // to store check if exists (find by pk), if not insert; if exists check lastUpdatedStamp: if null or before the candidate value insert, otherwise don't insert
201

202                 // maintain the original timestamps when doing storage of synced data, by default with will update the timestamps to now
203
valueToStore.setIsFromEntitySync(true);
204
205                 // check to make sure all foreign keys are created; if not create dummy values as place holders
206
valueToStore.checkFks(true);
207
208                 GenericValue existingValue = delegator.findByPrimaryKey(valueToStore.getPrimaryKey());
209                 if (existingValue == null) {
210                     delegator.create(valueToStore);
211                     toStoreInserted++;
212                 } else {
213                     // if the existing value has a stamp field that is AFTER the stamp on the valueToStore, don't update it
214
if (existingValue.get(ModelEntity.STAMP_FIELD) != null && existingValue.getTimestamp(ModelEntity.STAMP_FIELD).after(valueToStore.getTimestamp(ModelEntity.STAMP_FIELD))) {
215                         toStoreNotUpdated++;
216                     } else {
217                         delegator.store(valueToStore);
218                         toStoreUpdated++;
219                     }
220                 }
221             }
222             
223             // iterate through to remove list and remove each
224
Iterator JavaDoc keyToRemoveIter = keysToRemove.iterator();
225             while (keyToRemoveIter.hasNext()) {
226                 GenericEntity pkToRemove = (GenericEntity) keyToRemoveIter.next();
227                 
228                 // check to see if it exists, if so remove and count, if not just count already removed
229
// always do a removeByAnd, if it was a removeByAnd great, if it was a removeByPrimaryKey, this will also work and save us a query
230
pkToRemove.setIsFromEntitySync(true);
231                 int numRemByAnd = delegator.removeByAnd(pkToRemove.getEntityName(), pkToRemove);
232                 if (numRemByAnd == 0) {
233                     toRemoveAlreadyDeleted++;
234                 } else {
235                     toRemoveDeleted++;
236                 }
237             }
238             
239             Map JavaDoc result = ServiceUtil.returnSuccess();
240             result.put("toCreateInserted", new Long JavaDoc(toCreateInserted));
241             result.put("toCreateUpdated", new Long JavaDoc(toCreateUpdated));
242             result.put("toCreateNotUpdated", new Long JavaDoc(toCreateNotUpdated));
243             result.put("toStoreInserted", new Long JavaDoc(toStoreInserted));
244             result.put("toStoreUpdated", new Long JavaDoc(toStoreUpdated));
245             result.put("toStoreNotUpdated", new Long JavaDoc(toStoreNotUpdated));
246             result.put("toRemoveDeleted", new Long JavaDoc(toRemoveDeleted));
247             result.put("toRemoveAlreadyDeleted", new Long JavaDoc(toRemoveAlreadyDeleted));
248             return result;
249         } catch (GenericEntityException e) {
250             String JavaDoc errorMsg = "Exception saving Entity Sync Data for entitySyncId [" + entitySyncId + "]: " + e.toString();
251             Debug.logError(e, errorMsg, module);
252             return ServiceUtil.returnError(errorMsg);
253         } catch (Throwable JavaDoc t) {
254             String JavaDoc errorMsg = "Error saving Entity Sync Data for entitySyncId [" + entitySyncId + "]: " + t.toString();
255             Debug.logError(t, errorMsg, module);
256             return ServiceUtil.returnError(errorMsg);
257         }
258     }
259
260     /**
261      * Run Pull Entity Sync - Pull From Remote
262      *@param dctx The DispatchContext that this service is operating in
263      *@param context Map containing the input parameters
264      *@return Map with the result of the service, the output parameters
265      */

266     public static Map JavaDoc runPullEntitySync(DispatchContext dctx, Map JavaDoc context) {
267         LocalDispatcher dispatcher = dctx.getDispatcher();
268         
269         String JavaDoc entitySyncId = (String JavaDoc) context.get("entitySyncId");
270         String JavaDoc remotePullAndReportEntitySyncDataName = (String JavaDoc) context.get("remotePullAndReportEntitySyncDataName");
271
272         Debug.logInfo("Running runPullEntitySync for entitySyncId=" + context.get("entitySyncId"), module);
273
274         // loop until no data is returned to store
275
boolean gotMoreData = true;
276         
277         Timestamp JavaDoc startDate = null;
278         Long JavaDoc toCreateInserted = null;
279         Long JavaDoc toCreateUpdated = null;
280         Long JavaDoc toCreateNotUpdated = null;
281         Long JavaDoc toStoreInserted = null;
282         Long JavaDoc toStoreUpdated = null;
283         Long JavaDoc toStoreNotUpdated = null;
284         Long JavaDoc toRemoveDeleted = null;
285         Long JavaDoc toRemoveAlreadyDeleted = null;
286         
287         while (gotMoreData) {
288             gotMoreData = false;
289             
290             // call pullAndReportEntitySyncData, initially with no results, then with results from last loop
291
Map JavaDoc remoteCallContext = new HashMap JavaDoc();
292             remoteCallContext.put("entitySyncId", entitySyncId);
293             remoteCallContext.put("delegatorName", context.get("remoteDelegatorName"));
294             remoteCallContext.put("userLogin", context.get("userLogin"));
295
296             remoteCallContext.put("startDate", startDate);
297             remoteCallContext.put("toCreateInserted", toCreateInserted);
298             remoteCallContext.put("toCreateUpdated", toCreateUpdated);
299             remoteCallContext.put("toCreateNotUpdated", toCreateNotUpdated);
300             remoteCallContext.put("toStoreInserted", toStoreInserted);
301             remoteCallContext.put("toStoreUpdated", toStoreUpdated);
302             remoteCallContext.put("toStoreNotUpdated", toStoreNotUpdated);
303             remoteCallContext.put("toRemoveDeleted", toRemoveDeleted);
304             remoteCallContext.put("toRemoveAlreadyDeleted", toRemoveAlreadyDeleted);
305             
306             try {
307                 Map JavaDoc result = dispatcher.runSync(remotePullAndReportEntitySyncDataName, remoteCallContext);
308                 if (ServiceUtil.isError(result)) {
309                     String JavaDoc errMsg = "Error calling remote pull and report EntitySync service with name: " + remotePullAndReportEntitySyncDataName;
310                     return ServiceUtil.returnError(errMsg, null, null, result);
311                 }
312                 
313                 startDate = (Timestamp JavaDoc) result.get("startDate");
314                 
315                 try {
316                     // store data returned, get results (just call storeEntitySyncData locally, get the numbers back and boom shakalaka)
317

318                     // anything to store locally?
319
if (startDate != null && (!UtilValidate.isEmpty((Collection JavaDoc) result.get("valuesToCreate")) ||
320                             !UtilValidate.isEmpty((Collection JavaDoc) result.get("valuesToStore")) ||
321                             !UtilValidate.isEmpty((Collection JavaDoc) result.get("keysToRemove")))) {
322                         
323                         // yep, we got more data
324
gotMoreData = true;
325
326                         // at least one of the is not empty, make sure none of them are null now too...
327
List JavaDoc valuesToCreate = (List JavaDoc) result.get("valuesToCreate");
328                         if (valuesToCreate == null) valuesToCreate = Collections.EMPTY_LIST;
329                         List JavaDoc valuesToStore = (List JavaDoc) result.get("valuesToStore");
330                         if (valuesToStore == null) valuesToStore = Collections.EMPTY_LIST;
331                         List JavaDoc keysToRemove = (List JavaDoc) result.get("keysToRemove");
332                         if (keysToRemove == null) keysToRemove = Collections.EMPTY_LIST;
333                         
334                         Map JavaDoc callLocalStoreContext = UtilMisc.toMap("entitySyncId", entitySyncId, "delegatorName", context.get("localDelegatorName"),
335                                 "valuesToCreate", valuesToCreate, "valuesToStore", valuesToStore,
336                                 "keysToRemove", keysToRemove);
337                         
338                         callLocalStoreContext.put("userLogin", context.get("userLogin"));
339                         Map JavaDoc storeResult = dispatcher.runSync("storeEntitySyncData", callLocalStoreContext);
340                         if (ServiceUtil.isError(storeResult)) {
341                             String JavaDoc errMsg = "Error calling service to store data locally";
342                             return ServiceUtil.returnError(errMsg, null, null, storeResult);
343                         }
344                         
345                         // get results for next pass
346
toCreateInserted = (Long JavaDoc) storeResult.get("toCreateInserted");
347                         toCreateUpdated = (Long JavaDoc) storeResult.get("toCreateUpdated");
348                         toCreateNotUpdated = (Long JavaDoc) storeResult.get("toCreateNotUpdated");
349                         toStoreInserted = (Long JavaDoc) storeResult.get("toStoreInserted");
350                         toStoreUpdated = (Long JavaDoc) storeResult.get("toStoreUpdated");
351                         toStoreNotUpdated = (Long JavaDoc) storeResult.get("toStoreNotUpdated");
352                         toRemoveDeleted = (Long JavaDoc) storeResult.get("toRemoveDeleted");
353                         toRemoveAlreadyDeleted = (Long JavaDoc) storeResult.get("toRemoveAlreadyDeleted");
354                     }
355                 } catch (GenericServiceException e) {
356                     String JavaDoc errMsg = "Error calling service to store data locally: " + e.toString();
357                     Debug.logError(e, errMsg, module);
358                     return ServiceUtil.returnError(errMsg);
359                 }
360             } catch (GenericServiceException e) {
361                 String JavaDoc errMsg = "Exception calling remote pull and report EntitySync service with name: " + remotePullAndReportEntitySyncDataName + "; " + e.toString();
362                 Debug.logError(e, errMsg, module);
363                 return ServiceUtil.returnError(errMsg);
364             } catch (Throwable JavaDoc t) {
365                 String JavaDoc errMsg = "Error calling remote pull and report EntitySync service with name: " + remotePullAndReportEntitySyncDataName + "; " + t.toString();
366                 Debug.logError(t, errMsg, module);
367                 return ServiceUtil.returnError(errMsg);
368             }
369         }
370         
371         return ServiceUtil.returnSuccess();
372     }
373
374     /**
375      * Pull and Report Entity Sync Data - Called Remotely to Push Results from last pull, the Pull next set of results.
376      *@param dctx The DispatchContext that this service is operating in
377      *@param context Map containing the input parameters
378      *@return Map with the result of the service, the output parameters
379      */

380     public static Map JavaDoc pullAndReportEntitySyncData(DispatchContext dctx, Map JavaDoc context) {
381         EntitySyncContext esc = null;
382         try {
383             esc = new EntitySyncContext(dctx, context);
384             
385             Debug.logInfo("Doing pullAndReportEntitySyncData for entitySyncId=" + esc.entitySyncId + ", currentRunStartTime=" + esc.currentRunStartTime + ", currentRunEndTime=" + esc.currentRunEndTime, module);
386             
387             if ("Y".equals(esc.entitySync.get("forPushOnly"))) {
388                 return ServiceUtil.returnError("Cannot do Entity Sync Pull because entitySyncId [] is set for Push Only.");
389             }
390
391             // Part 1: if any results are passed, store the results for the given startDate, update EntitySync, etc
392
// restore info from last pull, or if no results start new run
393
esc.runPullStartOrRestoreSavedResults();
394             
395
396             // increment starting time to run until now
397
while (esc.hasMoreTimeToSync()) {
398                 // make sure the following message is commented out before commit:
399
// Debug.logInfo("(loop)Doing pullAndReportEntitySyncData split, currentRunStartTime=" + esc.currentRunStartTime + ", currentRunEndTime=" + esc.currentRunEndTime, module);
400

401                 esc.totalSplits++;
402                 
403                 // tx times are indexed
404
// keep track of how long these sync runs take and store that info on the history table
405
// saves info about removed, all entities that don't have no-auto-stamp set, this will be done in the GenericDAO like the stamp sets
406

407                 // Part 2: get the next set of data for the given entitySyncId
408
// Part 2a: return it back for storage but leave the EntitySyncHistory without results, and don't update the EntitySync last time
409

410                 // ===== INSERTS =====
411
ArrayList JavaDoc valuesToCreate = esc.assembleValuesToCreate();
412                 // ===== UPDATES =====
413
ArrayList JavaDoc valuesToStore = esc.assembleValuesToStore();
414                 // ===== DELETES =====
415
List JavaDoc keysToRemove = esc.assembleKeysToRemove();
416                 
417                 esc.setTotalRowCounts(valuesToCreate, valuesToStore, keysToRemove);
418
419                 if (Debug.infoOn()) Debug.logInfo("Service pullAndReportEntitySyncData returning - [" + valuesToCreate.size() + "] to create; [" + valuesToStore.size() + "] to store; [" + keysToRemove.size() + "] to remove; [" + esc.totalRowsPerSplit + "] total rows per split.", module);
420                 if (esc.totalRowsPerSplit > 0) {
421                     // stop if we found some data, otherwise look and try again
422
Map JavaDoc result = ServiceUtil.returnSuccess();
423                     result.put("startDate", esc.startDate);
424                     result.put("valuesToCreate", valuesToCreate);
425                     result.put("valuesToStore", valuesToStore);
426                     result.put("keysToRemove", keysToRemove);
427                     return result;
428                 } else {
429                     // save the progress to EntitySync and EntitySyncHistory, and move on...
430
esc.saveResultsReportedFromDataStore();
431                     esc.advanceRunTimes();
432                 }
433             }
434             
435             // if no more results from database to return, save final settings
436
if (!esc.hasMoreTimeToSync() ) {
437                 esc.saveFinalSyncResults();
438             }
439         } catch (SyncAbortException e) {
440             return e.returnError(module);
441         } catch (SyncErrorException e) {
442             e.saveSyncErrorInfo(esc);
443             return e.returnError(module);
444         }
445         return ServiceUtil.returnSuccess();
446     }
447
448     public static Map JavaDoc runOfflineEntitySync(DispatchContext dctx, Map JavaDoc context) {
449         String JavaDoc fileName = (String JavaDoc) context.get("fileName");
450         EntitySyncContext esc = null;
451         long totalRowsExported = 0;
452         try {
453             esc = new EntitySyncContext(dctx, context);
454
455             Debug.logInfo("Doing runManualEntitySync for entitySyncId=" + esc.entitySyncId + ", currentRunStartTime=" + esc.currentRunStartTime + ", currentRunEndTime=" + esc.currentRunEndTime, module);
456             Document JavaDoc mainDoc = UtilXml.makeEmptyXmlDocument("xml-entity-synchronization");
457             Element JavaDoc docElement = mainDoc.getDocumentElement();
458             docElement.setAttribute("xml:lang", "en-US");
459             esc.runOfflineStartRunning();
460
461             // increment starting time to run until now
462
esc.setSplitStartTime(); // just run this the first time, will be updated between each loop automatically
463

464             while (esc.hasMoreTimeToSync()) {
465                 esc.totalSplits++;
466
467                 ArrayList JavaDoc valuesToCreate = esc.assembleValuesToCreate();
468                 ArrayList JavaDoc valuesToStore = esc.assembleValuesToStore();
469                 List JavaDoc keysToRemove = esc.assembleKeysToRemove();
470
471                 long currentRows = esc.setTotalRowCounts(valuesToCreate, valuesToStore, keysToRemove);
472                 totalRowsExported += currentRows;
473
474                 if (currentRows > 0) {
475                     // create the XML document
476
Element JavaDoc syncElement = UtilXml.addChildElement(docElement, "entity-sync", mainDoc);
477                     syncElement.setAttribute("entitySyncId", esc.entitySyncId);
478                     syncElement.setAttribute("lastSuccessfulSynchTime", esc.currentRunEndTime.toString());
479
480                     // serialize the list data for XML storage
481
try {
482                         UtilXml.addChildElementValue(syncElement, "values-to-create", XmlSerializer.serialize(valuesToCreate), mainDoc);
483                         UtilXml.addChildElementValue(syncElement, "values-to-store", XmlSerializer.serialize(valuesToStore), mainDoc);
484                         UtilXml.addChildElementValue(syncElement, "keys-to-remove", XmlSerializer.serialize(keysToRemove), mainDoc);
485                     } catch (SerializeException e) {
486                         throw new EntitySyncContext.SyncOtherErrorException("List serialization problem", e);
487                     } catch (IOException JavaDoc e) {
488                         throw new EntitySyncContext.SyncOtherErrorException("XML writing problem", e);
489                     }
490                 }
491
492                 // update the result info
493
esc.runSaveOfflineSyncInfo(currentRows);
494                 esc.advanceRunTimes();
495             }
496
497             if (totalRowsExported > 0) {
498                 // check the file name; use a default if none is passed in
499
if (UtilValidate.isEmpty(fileName)) {
500                     SimpleDateFormat JavaDoc sdf = new SimpleDateFormat JavaDoc("yyyyMMddHHmmss");
501                     fileName = "offline_entitySync-" + esc.entitySyncId + "-" + sdf.format(new Date JavaDoc()) + ".xml";
502                 }
503
504                 // write the XML file
505
try {
506                     UtilXml.writeXmlDocument(fileName, mainDoc);
507                 } catch (java.io.FileNotFoundException JavaDoc e) {
508                     throw new EntitySyncContext.SyncOtherErrorException(e);
509                 } catch (java.io.IOException JavaDoc e) {
510                     throw new EntitySyncContext.SyncOtherErrorException(e);
511                 }
512             } else {
513                 Debug.logInfo("No rows to write; no data exported.", module);
514             }
515
516             // save the final results
517
esc.saveFinalSyncResults();
518         } catch (SyncAbortException e) {
519             return e.returnError(module);
520         } catch (SyncErrorException e) {
521             e.saveSyncErrorInfo(esc);
522             return e.returnError(module);
523         }
524
525         return ServiceUtil.returnSuccess();
526     }
527
528     public static Map JavaDoc loadOfflineSyncData(DispatchContext dctx, Map JavaDoc context) {
529         LocalDispatcher dispatcher = dctx.getDispatcher();
530         GenericDelegator delegator = dctx.getDelegator();
531         GenericValue userLogin = (GenericValue) context.get("userLogin");
532         String JavaDoc fileName = (String JavaDoc) context.get("xmlFileName");
533
534         URL JavaDoc xmlFile = UtilURL.fromResource(fileName);
535         if (xmlFile != null) {
536             Document JavaDoc xmlSyncDoc = null;
537             try {
538                 xmlSyncDoc = UtilXml.readXmlDocument(xmlFile, false);
539             } catch (SAXException JavaDoc e) {
540                 Debug.logError(e, module);
541             } catch (ParserConfigurationException JavaDoc e) {
542                 Debug.logError(e, module);
543             } catch (IOException JavaDoc e) {
544                 Debug.logError(e, module);
545             }
546             if (xmlSyncDoc == null) {
547                 return ServiceUtil.returnError("EntitySync XML document (" + fileName + ") is not valid!");
548             }
549
550             List JavaDoc syncElements = UtilXml.childElementList(xmlSyncDoc.getDocumentElement());
551             if (syncElements != null) {
552                 Iterator JavaDoc i = syncElements.iterator();
553                 while (i.hasNext()) {
554                     Element JavaDoc entitySync = (Element JavaDoc) i.next();
555                     String JavaDoc entitySyncId = entitySync.getAttribute("entitySyncId");
556                     String JavaDoc startTime = entitySync.getAttribute("lastSuccessfulSynchTime");
557
558                     String JavaDoc createString = UtilXml.childElementValue(entitySync, "values-to-create");
559                     String JavaDoc storeString = UtilXml.childElementValue(entitySync, "values-to-store");
560                     String JavaDoc removeString = UtilXml.childElementValue(entitySync, "keys-to-remove");
561
562                     // de-serialize the value lists
563
try {
564                         List JavaDoc valuesToCreate = (List JavaDoc) XmlSerializer.deserialize(createString, delegator);
565                         List JavaDoc valuesToStore = (List JavaDoc) XmlSerializer.deserialize(storeString, delegator);
566                         List JavaDoc keysToRemove = (List JavaDoc) XmlSerializer.deserialize(removeString, delegator);
567
568                         Map JavaDoc storeContext = UtilMisc.toMap("entitySyncId", entitySyncId, "valuesToCreate", valuesToCreate,
569                                 "valuesToStore", valuesToStore, "keysToRemove", keysToRemove, "userLogin", userLogin);
570
571                         // store the value(s)
572
Map JavaDoc storeResult = dispatcher.runSync("storeEntitySyncData", storeContext);
573                         if (ServiceUtil.isError(storeResult)) {
574                             throw new Exception JavaDoc(ServiceUtil.getErrorMessage(storeResult));
575                         }
576
577                         // TODO create a response document to send back to the initial sync machine
578
} catch (Exception JavaDoc e) {
579                         return ServiceUtil.returnError("Unable to load EntitySync XML [" + entitySyncId + "] - Problem at '" +
580                                     startTime + "' Error: " + e.getMessage());
581                     }
582                 }
583             }
584         } else {
585             return ServiceUtil.returnError("Offline EntitySync XML file not found (" + fileName + ")");
586         }
587
588         return ServiceUtil.returnSuccess();
589     }
590
591     public static Map JavaDoc updateOfflineEntitySync(DispatchContext dctx, Map JavaDoc context) {
592         return ServiceUtil.returnError("Service not yet implemented.");
593     }
594
595     /**
596      * Clean EntitySyncRemove Info
597      *@param dctx The DispatchContext that this service is operating in
598      *@param context Map containing the input parameters
599      *@return Map with the result of the service, the output parameters
600      */

601     public static Map JavaDoc cleanSyncRemoveInfo(DispatchContext dctx, Map JavaDoc context) {
602         Debug.logInfo("Running cleanSyncRemoveInfo", module);
603         GenericDelegator delegator = dctx.getDelegator();
604         
605         try {
606             // find the largest keepRemoveInfoHours value on an EntitySyncRemove and kill everything before that, if none found default to 10 days (240 hours)
607
double keepRemoveInfoHours = 24;
608             
609             List JavaDoc entitySyncRemoveList = delegator.findAll("EntitySync");
610             Iterator JavaDoc entitySyncRemoveIter = entitySyncRemoveList.iterator();
611             while (entitySyncRemoveIter.hasNext()) {
612                 GenericValue entitySyncRemove = (GenericValue) entitySyncRemoveIter.next();
613                 Double JavaDoc curKrih = entitySyncRemove.getDouble("keepRemoveInfoHours");
614                 if (curKrih != null) {
615                     double curKrihVal = curKrih.doubleValue();
616                     if (curKrihVal > keepRemoveInfoHours) {
617                         keepRemoveInfoHours = curKrihVal;
618                     }
619                 }
620             }
621             
622             
623             int keepSeconds = (int) Math.floor(keepRemoveInfoHours * 60);
624             
625             Calendar JavaDoc nowCal = Calendar.getInstance();
626             nowCal.setTimeInMillis(System.currentTimeMillis());
627             nowCal.add(Calendar.SECOND, -keepSeconds);
628             Timestamp JavaDoc keepAfterStamp = new Timestamp JavaDoc(nowCal.getTimeInMillis());
629             
630             int numRemoved = delegator.removeByCondition("EntitySyncRemove", new EntityExpr(ModelEntity.STAMP_TX_FIELD, EntityOperator.LESS_THAN, keepAfterStamp));
631             Debug.logInfo("In cleanSyncRemoveInfo removed [" + numRemoved + "] values with TX timestamp before [" + keepAfterStamp + "]", module);
632             
633             return ServiceUtil.returnSuccess();
634         } catch (GenericEntityException e) {
635             String JavaDoc errorMsg = "Error cleaning out EntitySyncRemove info: " + e.toString();
636             Debug.logError(e, errorMsg, module);
637             return ServiceUtil.returnError(errorMsg);
638         }
639     }
640 }
641
Popular Tags