1 25 package org.ofbiz.entityext.synchronization; 26 27 import java.sql.Timestamp ; 28 import java.util.ArrayList ; 29 import java.util.Calendar ; 30 import java.util.Collection ; 31 import java.util.Collections ; 32 import java.util.HashMap ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.Map ; 36 import java.util.Date ; 37 import java.text.SimpleDateFormat ; 38 import java.io.IOException ; 39 import java.net.URL ; 40 41 import javax.xml.parsers.ParserConfigurationException ; 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 ; 65 import org.w3c.dom.Element ; 66 import org.xml.sax.SAXException ; 67 68 76 public class EntitySyncServices { 77 78 public static final String module = EntitySyncServices.class.getName(); 79 80 86 public static Map runEntitySync(DispatchContext dctx, Map 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 esc.setSplitStartTime(); while (esc.hasMoreTimeToSync()) { 99 100 103 esc.totalSplits++; 104 105 109 ArrayList valuesToCreate = esc.assembleValuesToCreate(); 111 ArrayList valuesToStore = esc.assembleValuesToStore(); 113 List 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 140 public static Map storeEntitySyncData(DispatchContext dctx, Map context) { 141 GenericDelegator delegator = dctx.getDelegator(); 142 String overrideDelegatorName = (String ) 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 151 String entitySyncId = (String ) context.get("entitySyncId"); 152 List valuesToCreate = (List ) context.get("valuesToCreate"); 154 List valuesToStore = (List ) context.get("valuesToStore"); 155 List keysToRemove = (List ) 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 Iterator valueToCreateIter = valuesToCreate.iterator(); 170 while (valueToCreateIter.hasNext()) { 171 GenericValue valueToCreate = (GenericValue) valueToCreateIter.next(); 172 175 valueToCreate.setIsFromEntitySync(true); 177 178 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 (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 Iterator valueToStoreIter = valuesToStore.iterator(); 198 while (valueToStoreIter.hasNext()) { 199 GenericValue valueToStore = (GenericValue) valueToStoreIter.next(); 200 202 valueToStore.setIsFromEntitySync(true); 204 205 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 (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 Iterator keyToRemoveIter = keysToRemove.iterator(); 225 while (keyToRemoveIter.hasNext()) { 226 GenericEntity pkToRemove = (GenericEntity) keyToRemoveIter.next(); 227 228 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 result = ServiceUtil.returnSuccess(); 240 result.put("toCreateInserted", new Long (toCreateInserted)); 241 result.put("toCreateUpdated", new Long (toCreateUpdated)); 242 result.put("toCreateNotUpdated", new Long (toCreateNotUpdated)); 243 result.put("toStoreInserted", new Long (toStoreInserted)); 244 result.put("toStoreUpdated", new Long (toStoreUpdated)); 245 result.put("toStoreNotUpdated", new Long (toStoreNotUpdated)); 246 result.put("toRemoveDeleted", new Long (toRemoveDeleted)); 247 result.put("toRemoveAlreadyDeleted", new Long (toRemoveAlreadyDeleted)); 248 return result; 249 } catch (GenericEntityException e) { 250 String 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 t) { 254 String 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 266 public static Map runPullEntitySync(DispatchContext dctx, Map context) { 267 LocalDispatcher dispatcher = dctx.getDispatcher(); 268 269 String entitySyncId = (String ) context.get("entitySyncId"); 270 String remotePullAndReportEntitySyncDataName = (String ) context.get("remotePullAndReportEntitySyncDataName"); 271 272 Debug.logInfo("Running runPullEntitySync for entitySyncId=" + context.get("entitySyncId"), module); 273 274 boolean gotMoreData = true; 276 277 Timestamp startDate = null; 278 Long toCreateInserted = null; 279 Long toCreateUpdated = null; 280 Long toCreateNotUpdated = null; 281 Long toStoreInserted = null; 282 Long toStoreUpdated = null; 283 Long toStoreNotUpdated = null; 284 Long toRemoveDeleted = null; 285 Long toRemoveAlreadyDeleted = null; 286 287 while (gotMoreData) { 288 gotMoreData = false; 289 290 Map remoteCallContext = new HashMap (); 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 result = dispatcher.runSync(remotePullAndReportEntitySyncDataName, remoteCallContext); 308 if (ServiceUtil.isError(result)) { 309 String 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 ) result.get("startDate"); 314 315 try { 316 318 if (startDate != null && (!UtilValidate.isEmpty((Collection ) result.get("valuesToCreate")) || 320 !UtilValidate.isEmpty((Collection ) result.get("valuesToStore")) || 321 !UtilValidate.isEmpty((Collection ) result.get("keysToRemove")))) { 322 323 gotMoreData = true; 325 326 List valuesToCreate = (List ) result.get("valuesToCreate"); 328 if (valuesToCreate == null) valuesToCreate = Collections.EMPTY_LIST; 329 List valuesToStore = (List ) result.get("valuesToStore"); 330 if (valuesToStore == null) valuesToStore = Collections.EMPTY_LIST; 331 List keysToRemove = (List ) result.get("keysToRemove"); 332 if (keysToRemove == null) keysToRemove = Collections.EMPTY_LIST; 333 334 Map 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 storeResult = dispatcher.runSync("storeEntitySyncData", callLocalStoreContext); 340 if (ServiceUtil.isError(storeResult)) { 341 String errMsg = "Error calling service to store data locally"; 342 return ServiceUtil.returnError(errMsg, null, null, storeResult); 343 } 344 345 toCreateInserted = (Long ) storeResult.get("toCreateInserted"); 347 toCreateUpdated = (Long ) storeResult.get("toCreateUpdated"); 348 toCreateNotUpdated = (Long ) storeResult.get("toCreateNotUpdated"); 349 toStoreInserted = (Long ) storeResult.get("toStoreInserted"); 350 toStoreUpdated = (Long ) storeResult.get("toStoreUpdated"); 351 toStoreNotUpdated = (Long ) storeResult.get("toStoreNotUpdated"); 352 toRemoveDeleted = (Long ) storeResult.get("toRemoveDeleted"); 353 toRemoveAlreadyDeleted = (Long ) storeResult.get("toRemoveAlreadyDeleted"); 354 } 355 } catch (GenericServiceException e) { 356 String 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 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 t) { 365 String 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 380 public static Map pullAndReportEntitySyncData(DispatchContext dctx, Map 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 esc.runPullStartOrRestoreSavedResults(); 394 395 396 while (esc.hasMoreTimeToSync()) { 398 401 esc.totalSplits++; 402 403 407 410 ArrayList valuesToCreate = esc.assembleValuesToCreate(); 412 ArrayList valuesToStore = esc.assembleValuesToStore(); 414 List 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 Map 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 esc.saveResultsReportedFromDataStore(); 431 esc.advanceRunTimes(); 432 } 433 } 434 435 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 runOfflineEntitySync(DispatchContext dctx, Map context) { 449 String fileName = (String ) 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 mainDoc = UtilXml.makeEmptyXmlDocument("xml-entity-synchronization"); 457 Element docElement = mainDoc.getDocumentElement(); 458 docElement.setAttribute("xml:lang", "en-US"); 459 esc.runOfflineStartRunning(); 460 461 esc.setSplitStartTime(); 464 while (esc.hasMoreTimeToSync()) { 465 esc.totalSplits++; 466 467 ArrayList valuesToCreate = esc.assembleValuesToCreate(); 468 ArrayList valuesToStore = esc.assembleValuesToStore(); 469 List keysToRemove = esc.assembleKeysToRemove(); 470 471 long currentRows = esc.setTotalRowCounts(valuesToCreate, valuesToStore, keysToRemove); 472 totalRowsExported += currentRows; 473 474 if (currentRows > 0) { 475 Element syncElement = UtilXml.addChildElement(docElement, "entity-sync", mainDoc); 477 syncElement.setAttribute("entitySyncId", esc.entitySyncId); 478 syncElement.setAttribute("lastSuccessfulSynchTime", esc.currentRunEndTime.toString()); 479 480 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 e) { 488 throw new EntitySyncContext.SyncOtherErrorException("XML writing problem", e); 489 } 490 } 491 492 esc.runSaveOfflineSyncInfo(currentRows); 494 esc.advanceRunTimes(); 495 } 496 497 if (totalRowsExported > 0) { 498 if (UtilValidate.isEmpty(fileName)) { 500 SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMddHHmmss"); 501 fileName = "offline_entitySync-" + esc.entitySyncId + "-" + sdf.format(new Date ()) + ".xml"; 502 } 503 504 try { 506 UtilXml.writeXmlDocument(fileName, mainDoc); 507 } catch (java.io.FileNotFoundException e) { 508 throw new EntitySyncContext.SyncOtherErrorException(e); 509 } catch (java.io.IOException e) { 510 throw new EntitySyncContext.SyncOtherErrorException(e); 511 } 512 } else { 513 Debug.logInfo("No rows to write; no data exported.", module); 514 } 515 516 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 loadOfflineSyncData(DispatchContext dctx, Map context) { 529 LocalDispatcher dispatcher = dctx.getDispatcher(); 530 GenericDelegator delegator = dctx.getDelegator(); 531 GenericValue userLogin = (GenericValue) context.get("userLogin"); 532 String fileName = (String ) context.get("xmlFileName"); 533 534 URL xmlFile = UtilURL.fromResource(fileName); 535 if (xmlFile != null) { 536 Document xmlSyncDoc = null; 537 try { 538 xmlSyncDoc = UtilXml.readXmlDocument(xmlFile, false); 539 } catch (SAXException e) { 540 Debug.logError(e, module); 541 } catch (ParserConfigurationException e) { 542 Debug.logError(e, module); 543 } catch (IOException 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 syncElements = UtilXml.childElementList(xmlSyncDoc.getDocumentElement()); 551 if (syncElements != null) { 552 Iterator i = syncElements.iterator(); 553 while (i.hasNext()) { 554 Element entitySync = (Element ) i.next(); 555 String entitySyncId = entitySync.getAttribute("entitySyncId"); 556 String startTime = entitySync.getAttribute("lastSuccessfulSynchTime"); 557 558 String createString = UtilXml.childElementValue(entitySync, "values-to-create"); 559 String storeString = UtilXml.childElementValue(entitySync, "values-to-store"); 560 String removeString = UtilXml.childElementValue(entitySync, "keys-to-remove"); 561 562 try { 564 List valuesToCreate = (List ) XmlSerializer.deserialize(createString, delegator); 565 List valuesToStore = (List ) XmlSerializer.deserialize(storeString, delegator); 566 List keysToRemove = (List ) XmlSerializer.deserialize(removeString, delegator); 567 568 Map storeContext = UtilMisc.toMap("entitySyncId", entitySyncId, "valuesToCreate", valuesToCreate, 569 "valuesToStore", valuesToStore, "keysToRemove", keysToRemove, "userLogin", userLogin); 570 571 Map storeResult = dispatcher.runSync("storeEntitySyncData", storeContext); 573 if (ServiceUtil.isError(storeResult)) { 574 throw new Exception (ServiceUtil.getErrorMessage(storeResult)); 575 } 576 577 } catch (Exception 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 updateOfflineEntitySync(DispatchContext dctx, Map context) { 592 return ServiceUtil.returnError("Service not yet implemented."); 593 } 594 595 601 public static Map cleanSyncRemoveInfo(DispatchContext dctx, Map context) { 602 Debug.logInfo("Running cleanSyncRemoveInfo", module); 603 GenericDelegator delegator = dctx.getDelegator(); 604 605 try { 606 double keepRemoveInfoHours = 24; 608 609 List entitySyncRemoveList = delegator.findAll("EntitySync"); 610 Iterator entitySyncRemoveIter = entitySyncRemoveList.iterator(); 611 while (entitySyncRemoveIter.hasNext()) { 612 GenericValue entitySyncRemove = (GenericValue) entitySyncRemoveIter.next(); 613 Double 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 nowCal = Calendar.getInstance(); 626 nowCal.setTimeInMillis(System.currentTimeMillis()); 627 nowCal.add(Calendar.SECOND, -keepSeconds); 628 Timestamp keepAfterStamp = new Timestamp (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 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 |