Related to 32899: Provide an base class which implements default offline and online behavior in multi-server environments
authorMartin Taal <martin.taal@openbravo.com>
Sun, 08 May 2016 01:15:47 +0200
changeset 5 68ba8744640d
parent 4 52f356c919ef
child 6 62fa2d61217e
Related to 32899: Provide an base class which implements default offline and online behavior in multi-server environments
Sample code for multi-server process calls
src-db/database/sourcedata/AD_REF_LIST.xml
src/org/openbravo/retail/storeserver/howto/CheckMultiServer.java
web/org.openbravo.retail.storeserver.howto/js/ob-storeserver-howto-multi-server-call.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src-db/database/sourcedata/AD_REF_LIST.xml	Sun May 08 01:15:47 2016 +0200
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<data>
+<!--7C21AA04184A4BC7A084B93C1A76FB50--><AD_REF_LIST>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <AD_REF_LIST_ID><![CDATA[7C21AA04184A4BC7A084B93C1A76FB50]]></AD_REF_LIST_ID>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <ISACTIVE><![CDATA[Y]]></ISACTIVE>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <VALUE><![CDATA[OBSTHOW_CheckMultiServer]]></VALUE>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <NAME><![CDATA[Check Multi Server]]></NAME>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <DESCRIPTION><![CDATA[Check Multi Server Example]]></DESCRIPTION>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <AD_REFERENCE_ID><![CDATA[11F86B630ECB4A57B28927193F8AB99D]]></AD_REFERENCE_ID>
+<!--7C21AA04184A4BC7A084B93C1A76FB50-->  <AD_MODULE_ID><![CDATA[ED6F408F7E85415A961E06275EDE3028]]></AD_MODULE_ID>
+<!--7C21AA04184A4BC7A084B93C1A76FB50--></AD_REF_LIST>
+
+</data>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/retail/storeserver/howto/CheckMultiServer.java	Sun May 08 01:15:47 2016 +0200
@@ -0,0 +1,222 @@
+/*
+ ************************************************************************************
+ * Copyright (C) 2015 Openbravo S.L.U.
+ * Licensed under the Openbravo Commercial License version 1.0
+ * You may obtain a copy of the License at http://www.openbravo.com/legal/obcl.html
+ * or in the legal folder of this module distribution.
+ ************************************************************************************
+ */
+package org.openbravo.retail.storeserver.howto;
+
+import java.util.Date;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import org.apache.log4j.Logger;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.hibernate.Query;
+import org.openbravo.base.exception.OBException;
+import org.openbravo.base.provider.OBProvider;
+import org.openbravo.base.weld.WeldUtils;
+import org.openbravo.client.kernel.RequestContext;
+import org.openbravo.dal.core.DalUtil;
+import org.openbravo.dal.core.OBContext;
+import org.openbravo.dal.service.OBDal;
+import org.openbravo.mobile.core.obmobcLogClient;
+import org.openbravo.mobile.core.process.JSONProcessSimple;
+import org.openbravo.mobile.core.process.MobileImportEntryProcessorRunnable;
+import org.openbravo.mobile.core.servercontroller.MobileServerController;
+import org.openbravo.mobile.core.servercontroller.MultiServerDataSynchronizationProcess;
+import org.openbravo.model.common.plm.Product;
+import org.openbravo.service.importprocess.ImportEntry;
+import org.openbravo.service.importprocess.ImportEntryManager.ImportEntryQualifier;
+import org.openbravo.service.importprocess.ImportEntryProcessor;
+
+/**
+ * Example code to show code that runs on the store server and replicates it actions to the central
+ * server. Also the other way is supported if the store server is not online.
+ */
+public class CheckMultiServer extends MultiServerDataSynchronizationProcess {
+  public static final Logger log = Logger.getLogger(CheckMultiServer.class);
+
+  @Override
+  protected String getImportEntryDataType() {
+    return "OBSTHOW_CheckMultiServer";
+  }
+
+  @Override
+  public JSONObject execute(JSONObject jsonRecord) {
+    try {
+      final JSONObject result = new JSONObject();
+      String msg = "";
+      if (MobileServerController.getInstance().isThisACentralServer()) {
+        msg = "This is a central server";
+      } else if (MobileServerController.getInstance().isThisAStoreServer()) {
+        msg = "This is a store server";
+      } else {
+        msg = "This is another server";
+      }
+      msg += " ("
+          + MobileServerController.getInstance().getThisServerDefinition().getMobileServerKey()
+          + ") ";
+      msg += " from source " + jsonRecord.getString(SOURCE_PROP);
+      msg += " with messageId " + jsonRecord.getString("messageId");
+      msg += " processed on " + new Date();
+      result.put("processTime", "" + new Date());
+
+      // some sample code to show the different cases
+      final String flow;
+      final String source = jsonRecord.getString(SOURCE_PROP);
+      if (SOURCE_WEBPOS.equals(source)) {
+        if (MobileServerController.getInstance().isThisACentralServer()) {
+          flow = "From WebPOS to Central";
+        } else if (MobileServerController.getInstance().isThisAStoreServer()) {
+          flow = "From WebPOS to Store";
+        } else {
+          flow = "From WebPOS to non-multi-server case";
+        }
+      } else if (SOURCE_STORE.equals(source)) {
+        if (getImportEntryId() != null) {
+          // this case happens when the import entry is replicated from the store
+          // to central, so not a direct webrequest
+          if (RequestContext.get().getRequest() != null) {
+            throw new OBException("Unexpected, direct webrequest, still import entry id is set");
+          }
+          flow = "From Store to Central using Import Entry Replication";
+        } else {
+          flow = "From Store to Central using direct webrequest";
+        }
+      } else if (SOURCE_CENTRAL.equals(source)) {
+        // this can only be an import entry replicated back
+        if (getImportEntryId() == null
+            || !MobileServerController.getInstance().isThisAStoreServer()) {
+          throw new OBException("Unexpected, import entry expected or this is not a store");
+        }
+        flow = "From Central to Store using import entry";
+      } else {
+        flow = "Unknown case..?";
+      }
+
+      msg += ". " + flow;
+      if (jsonRecord.has(RESULT_PROP)) {
+        final JSONObject prevResult = jsonRecord.getJSONObject(RESULT_PROP);
+
+        msg += ". Message was already processed " + prevResult;
+      }
+      result.put("msg", msg);
+
+      result.put("productInfo", getProductInfo(jsonRecord));
+
+      // as an example of an db-action create a log client record
+      obmobcLogClient logClient = OBProvider.getInstance().get(obmobcLogClient.class);
+      logClient.setDeviceId("OBSTHOW");
+      logClient.setLoglevel("Info");
+      logClient.setMsg(result.toString());
+      logClient.setOnline(true);
+      OBDal.getInstance().save(logClient);
+      return result;
+    } catch (JSONException e) {
+      throw new OBException(e);
+    }
+  }
+
+  protected String getProductInfo(JSONObject json) {
+    try {
+      String productId = json.getString("productId");
+      String hqlCheckProduct = "select p from Product as p where p.id='" + productId + "' ";
+      Query checkProductQuery = OBDal.getInstance().getSession().createQuery(hqlCheckProduct);
+
+      final StringBuilder productInfo = new StringBuilder();
+      for (Object o : checkProductQuery.list()) {
+        Product p = (Product) o;
+        productInfo.append("Product " + p.getSearchKey());
+      }
+      return productInfo.toString();
+    } catch (JSONException e) {
+      throw new OBException(e);
+    }
+  }
+
+  @ImportEntryQualifier(entity = "OBSTHOW_CheckMultiServer")
+  @ApplicationScoped
+  public static class MultiServerJsonHowToImportEntryProcessor extends ImportEntryProcessor {
+
+    protected ImportEntryProcessRunnable createImportEntryProcessRunnable() {
+      return WeldUtils.getInstanceFromStaticBeanManager(SynchronizedProcessRunnable.class);
+    }
+
+    protected boolean canHandleImportEntry(ImportEntry importEntryInformation) {
+      return "OBSTHOW_CheckMultiServer".equals(importEntryInformation.getTypeofdata());
+    }
+
+    protected String getProcessSelectionKey(ImportEntry importEntry) {
+      return (String) DalUtil.getId(importEntry.getOrganization());
+    }
+  }
+
+  /**
+   * An example of an import entry processor which shows how to also check that things are processed
+   * in order with a countEntries query and check for previous errors also.
+   * 
+   * @author mtaal
+   */
+  private static class SynchronizedProcessRunnable extends MobileImportEntryProcessorRunnable {
+
+    @Override
+    protected Class<? extends JSONProcessSimple> getJSONProcessorClass() {
+      return CheckMultiServer.class;
+    }
+
+    protected void processEntry(ImportEntry importEntry) throws Exception {
+      // check that there are no other import entries for the terminal
+      // which have not yet been processed
+
+      try {
+        OBContext.setAdminMode(false);
+        if (thereIsDataInImportQueue(importEntry)) {
+          // close and commit
+          OBDal.getInstance().commitAndClose();
+          return;
+        }
+
+      } finally {
+        OBContext.restorePreviousMode();
+      }
+      super.processEntry(importEntry);
+    }
+
+    private boolean thereIsDataInImportQueue(ImportEntry importEntry) {
+      try {
+        OBContext.setAdminMode(false);
+
+        if (0 < countEntries("Error", importEntry)) {
+          // if there are related error entries before this one then this is an error
+          // throw an exception to move this entry also to error status
+          throw new OBException("There are error records before this record " + importEntry
+              + ", moving this entry also to error status.");
+        }
+
+        return 0 < countEntries("Initial", importEntry);
+      } finally {
+        OBContext.restorePreviousMode();
+      }
+    }
+
+    private int countEntries(String importStatus, ImportEntry importEntry) {
+      final String whereClause = ImportEntry.PROPERTY_IMPORTSTATUS + "='" + importStatus + "' and "
+          + ImportEntry.PROPERTY_TYPEOFDATA + "='OBSTHOW_SynchronizedData' and "
+          + ImportEntry.PROPERTY_CREATIONDATE + "<:creationDate and "
+          + ImportEntry.PROPERTY_ORGANIZATION + "=:org and id!=:id";
+      final Query qry = OBDal.getInstance().getSession()
+          .createQuery("select count(*) from " + ImportEntry.ENTITY_NAME + " where " + whereClause);
+      qry.setParameter("id", importEntry.getId());
+      qry.setTimestamp("creationDate", importEntry.getCreationDate());
+      qry.setParameter("org", importEntry.getOrganization());
+
+      return ((Number) qry.uniqueResult()).intValue();
+    }
+
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/org.openbravo.retail.storeserver.howto/js/ob-storeserver-howto-multi-server-call.js	Sun May 08 01:15:47 2016 +0200
@@ -0,0 +1,50 @@
+/*
+ ************************************************************************************
+ * Copyright (C) 2015 Openbravo S.L.U.
+ * Licensed under the Openbravo Commercial License version 1.0
+ * You may obtain a copy of the License at http://www.openbravo.com/legal/obcl.html
+ * or in the legal folder of this module distribution.
+ ************************************************************************************
+ */
+
+/*global enyo, Backbone, $, _ */
+
+(function () {
+
+  enyo.kind({
+    kind: 'OB.UI.SmallButton',
+    name: 'OB.OBPOSPointOfSale.UI.EditLine.CheckMultiServer',
+    content: 'Check Multi Server',
+    classes: 'btnlink-orange',
+    tap: function () {
+      var process = new OB.DS.Process('org.openbravo.retail.storeserver.howto.CheckMultiServer');
+      process.exec({
+
+        // it is best to set a messageId from the client so when communicating between servers
+        // duplicates are detected
+        messageId: OB.UTIL.get_UUID(),
+
+        // the message content must be in the data property, data may also be an array
+        productId: this.owner.owner.line.get('product').get('id')
+      }, function (data) {
+        if (data.exception) {
+          OB.UTIL.showError('Error occurred ' + data.exception.message);
+        } else {
+          OB.UTIL.showConfirmation.display('Multi Server Call Done', 'Call to server result: product info: ' + data.productInfo + ' with message ' + data.msg, [{
+            label: OB.I18N.getLabel('OBMOBC_LblOk'),
+            isConfirmButton: true
+          }], {
+            autoDismiss: false,
+          });
+        }
+      });
+    }
+  });
+
+  //Register the button...
+  OB.OBPOSPointOfSale.UI.EditLine.prototype.actionButtons.unshift({
+    kind: 'OB.OBPOSPointOfSale.UI.EditLine.CheckMultiServer',
+    name: 'checkMultiServer'
+  });
+
+}());
\ No newline at end of file