svn commit: r1621696 [7/12] - in /ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23: ./ applications/order/webapp/ordermgr/WEB-INF/actions/return/ applications/party/webapp/partymgr/party/contactmechtemplates/ applications/party/widget/partymgr/...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1621696 [7/12] - in /ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23: ./ applications/order/webapp/ordermgr/WEB-INF/actions/return/ applications/party/webapp/partymgr/party/contactmechtemplates/ applications/party/widget/partymgr/...

jleroux@apache.org
Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java Mon Sep  1 07:29:23 2014
@@ -22,14 +22,12 @@ import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.util.Hashtable;
-import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import javax.transaction.Transaction;
 
 import org.ofbiz.base.util.Debug;
-import org.ofbiz.base.util.UtilProperties;
-import org.ofbiz.entity.GenericDelegator;
 import org.ofbiz.entity.GenericEntityException;
 import org.ofbiz.entity.datasource.GenericHelperInfo;
 import org.ofbiz.entity.model.ModelEntity;
@@ -40,20 +38,18 @@ import org.ofbiz.entity.transaction.Tran
 
 /**
  * Sequence Utility to get unique sequences from named sequence banks
- * Uses a collision detection approach to safely get unique sequenced ids in banks from the database
  */
 public class SequenceUtil {
 
     public static final String module = SequenceUtil.class.getName();
 
-    private final Map<String, SequenceBank> sequences = new Hashtable<String, SequenceBank>();
+    private final ConcurrentMap<String, SequenceBank> sequences = new ConcurrentHashMap<String, SequenceBank>();
     private final GenericHelperInfo helperInfo;
     private final String tableName;
     private final String nameColName;
     private final String idColName;
-    private final boolean clustered;
 
-    public SequenceUtil(GenericDelegator delegator, GenericHelperInfo helperInfo, ModelEntity seqEntity, String nameFieldName, String idFieldName) {
+    public SequenceUtil(GenericHelperInfo helperInfo, ModelEntity seqEntity, String nameFieldName, String idFieldName) {
         this.helperInfo = helperInfo;
         if (seqEntity == null) {
             throw new IllegalArgumentException("The sequence model entity was null but is required.");
@@ -73,7 +69,6 @@ public class SequenceUtil {
             throw new IllegalArgumentException("Could not find the field definition for the sequence id field " + idFieldName);
         }
         this.idColName = idField.getColName();
-        clustered = delegator.useDistributedCacheClear() || "Y".equals(UtilProperties.getPropertyValue("general.properties", "clustered"));                
     }
 
     public Long getNextSeqId(String seqName, long staggerMax, ModelEntity seqModelEntity) {
@@ -95,18 +90,14 @@ public class SequenceUtil {
         SequenceBank bank = sequences.get(seqName);
 
         if (bank == null) {
-            synchronized(this) {
-                bank = sequences.get(seqName);
-                if (bank == null) {
-                    long bankSize = SequenceBank.defaultBankSize;
-                    if (seqModelEntity != null && seqModelEntity.getSequenceBankSize() != null) {
-                        bankSize = seqModelEntity.getSequenceBankSize().longValue();
-                        if (bankSize > SequenceBank.maxBankSize) bankSize = SequenceBank.maxBankSize;
-                    }
-                    bank = new SequenceBank(seqName, bankSize);
-                    sequences.put(seqName, bank);
-                }
+            long bankSize = SequenceBank.defaultBankSize;
+            if (seqModelEntity != null && seqModelEntity.getSequenceBankSize() != null) {
+                bankSize = seqModelEntity.getSequenceBankSize().longValue();
+                if (bankSize > SequenceBank.maxBankSize) bankSize = SequenceBank.maxBankSize;
             }
+            bank = new SequenceBank(seqName, bankSize);
+            SequenceBank bankFromCache = sequences.putIfAbsent(seqName, bank);
+            bank = bankFromCache != null ? bankFromCache : bank;
         }
 
         return bank;
@@ -116,55 +107,55 @@ public class SequenceUtil {
         public static final long defaultBankSize = 10;
         public static final long maxBankSize = 5000;
         public static final long startSeqId = 10000;
-        public static final long minWaitMillis = 5;
-        public static final long maxWaitMillis = 50;
-        public static final int maxTries = 5;
 
-        private long curSeqId;
-        private long maxSeqId;
         private final String seqName;
         private final long bankSize;
+        private final String updateForLockStatement;
+        private final String selectSequenceStatement;
+
+        private long curSeqId;
+        private long maxSeqId;
 
         private SequenceBank(String seqName, long bankSize) {
             this.seqName = seqName;
             curSeqId = 0;
             maxSeqId = 0;
             this.bankSize = bankSize;
-            fillBank(1);
+            updateForLockStatement = "UPDATE " + SequenceUtil.this.tableName + " SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
+            selectSequenceStatement = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
         }
 
-        private synchronized Long getNextSeqId(long staggerMax) {
+        private Long getNextSeqId(long staggerMax) {
             long stagger = 1;
             if (staggerMax > 1) {
-                stagger = Math.round(Math.random() * staggerMax);
+                stagger = (long)Math.ceil(Math.random() * staggerMax);
                 if (stagger == 0) stagger = 1;
             }
-
-            if ((curSeqId + stagger) <= maxSeqId) {
-                Long retSeqId = Long.valueOf(curSeqId);
-                curSeqId += stagger;
-                return retSeqId;
-            } else {
-                fillBank(stagger);
+            synchronized (this) {
                 if ((curSeqId + stagger) <= maxSeqId) {
-                    Long retSeqId = Long.valueOf(curSeqId);
+                    long retSeqId = curSeqId;
                     curSeqId += stagger;
                     return retSeqId;
                 } else {
-                    Debug.logError("[SequenceUtil.SequenceBank.getNextSeqId] Fill bank failed, returning null", module);
-                    return null;
+                    fillBank(stagger);
+                    if ((curSeqId + stagger) <= maxSeqId) {
+                        long retSeqId = curSeqId;
+                        curSeqId += stagger;
+                        return retSeqId;
+                    } else {
+                        Debug.logError("Fill bank failed, returning null", module);
+                        return null;
+                    }
                 }
             }
         }
 
-        private void refresh(long staggerMax) {
+        private synchronized void refresh(long staggerMax) {
             this.curSeqId = this.maxSeqId;
             this.fillBank(staggerMax);
         }
 
-        private synchronized void fillBank(long stagger) {
-            //Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Starting fillBank Thread Name is: " + Thread.currentThread().getName() + ":" + Thread.currentThread().toString(), module);
-
+        private void fillBank(long stagger) {
             // no need to get a new bank, SeqIds available
             if ((curSeqId + stagger) <= maxSeqId) return;
 
@@ -176,178 +167,117 @@ public class SequenceUtil {
 
             if (bankSize > maxBankSize) bankSize = maxBankSize;
 
-            long val1 = 0;
-            long val2 = 0;
+            Transaction suspendedTransaction = null;
+            try {
+                suspendedTransaction = TransactionUtil.suspend();
+
+                boolean beganTransaction = false;
+                try {
+                    beganTransaction = TransactionUtil.begin();
+
+                    Connection connection = null;
+                    Statement stmt = null;
+                    ResultSet rs = null;
 
-            // NOTE: the fancy ethernet type stuff is for the case where transactions not available, or something funny happens with really sensitive timing (between first select and update, for example)
-            int numTries = 0;
-
-            while (val1 + bankSize != val2) {
-                if (Debug.verboseOn()) Debug.logVerbose("[SequenceUtil.SequenceBank.fillBank] Trying to get a bank of sequenced ids for " +
-                        this.seqName + "; start of loop val1=" + val1 + ", val2=" + val2 + ", bankSize=" + bankSize, module);
-
-                // not sure if this synchronized block is totally necessary, the method is synchronized but it does do a wait/sleep
-                // outside of this block, and this is the really sensitive block, so making sure it is isolated; there is some overhead
-                // to this, but very bad things can happen if we try to do too many of these at once for a single sequencer
-                synchronized (this) {
-                    Transaction suspendedTransaction = null;
                     try {
-                        //if we can suspend the transaction, we'll try to do this in a local manual transaction
-                        suspendedTransaction = TransactionUtil.suspend();
-
-                        boolean beganTransaction = false;
-                        try {
-                            beganTransaction = TransactionUtil.begin();
-
-                            Connection connection = null;
-                            Statement stmt = null;
-                            ResultSet rs = null;
-
-                            try {
-                                connection = TransactionFactoryLoader.getInstance().getConnection(SequenceUtil.this.helperInfo);
-                            } catch (SQLException sqle) {
-                                Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database... Error was:" + sqle.toString(), module);
-                                throw sqle;
-                            } catch (GenericEntityException e) {
-                                Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database... Error was: " + e.toString(), module);
-                                throw e;
-                            }
-
-                            if (connection == null) {
-                                throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database, connection was null...");
-                            }
-
-                            String sql = null;
-
-                            try {
-                                // we shouldn't need this, and some TX managers complain about it, so not including it: connection.setAutoCommit(false);
-
-                                stmt = connection.createStatement();
-                                if (clustered) {
-                                    sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'" + " FOR UPDATE";                                    
-                                } else {
-                                    sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
-                                }
-                                rs = stmt.executeQuery(sql);
-                                boolean gotVal1 = false;
-                                if (rs.next()) {
-                                    val1 = rs.getLong(SequenceUtil.this.idColName);
-                                    gotVal1 = true;
-                                }
-                                rs.close();
-
-                                if (!gotVal1) {
-                                    Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] first select failed: will try to add new row, result set was empty for sequence [" + seqName + "] \nUsed SQL: " + sql + " \n Thread Name is: " + Thread.currentThread().getName() + ":" + Thread.currentThread().toString(), module);
-                                    sql = "INSERT INTO " + SequenceUtil.this.tableName + " (" + SequenceUtil.this.nameColName + ", " + SequenceUtil.this.idColName + ") VALUES ('" + this.seqName + "', " + startSeqId + ")";
-                                    if (stmt.executeUpdate(sql) <= 0) {
-                                        throw new GenericEntityException("No rows changed when trying insert new sequence row with this SQL: " + sql);
-                                    }
-                                    continue;
-                                }
-
-                                sql = "UPDATE " + SequenceUtil.this.tableName + " SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + "+" + bankSize + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
-                                if (stmt.executeUpdate(sql) <= 0) {
-                                    throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank] update failed, no rows changes for seqName: " + seqName);
-                                }
-                                if (clustered) {
-                                    sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'" + " FOR UPDATE";                                    
-
-                                } else {
-                                    sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
-                                }
-                                rs = stmt.executeQuery(sql);
-                                boolean gotVal2 = false;
-                                if (rs.next()) {
-                                    val2 = rs.getLong(SequenceUtil.this.idColName);
-                                    gotVal2 = true;
-                                }
-
-                                rs.close();
+                        connection = TransactionFactoryLoader.getInstance().getConnection(SequenceUtil.this.helperInfo);
+                    } catch (SQLException sqle) {
+                        Debug.logWarning("Unable to esablish a connection with the database. Error was:" + sqle.toString(), module);
+                        throw sqle;
+                    } catch (GenericEntityException e) {
+                        Debug.logWarning("Unable to esablish a connection with the database. Error was: " + e.toString(), module);
+                        throw e;
+                    }
+                    if (connection == null) {
+                        throw new GenericEntityException("Unable to esablish a connection with the database, connection was null...");
+                    }
 
-                                if (!gotVal2) {
-                                    throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank] second select failed: aborting, result set was empty for sequence: " + seqName);
-                                }
+                    String sql = null;
+                    try {
+                        stmt = connection.createStatement();
 
-                                // got val1 and val2 at this point, if we don't have the right difference between them, force a rollback (with
-                                //setRollbackOnly and NOT with an exception because we don't want to break from the loop, just err out and
-                                //continue), then flow out to allow the wait and loop thing to happen
-                                if (val1 + bankSize != val2) {
-                                    TransactionUtil.setRollbackOnly("Forcing transaction rollback in sequence increment because we didn't get a clean update, ie a conflict was found, so not saving the results", null);
-                                }
-                            } catch (SQLException sqle) {
-                                Debug.logWarning(sqle, "[SequenceUtil.SequenceBank.fillBank] SQL Exception while executing the following:\n" + sql + "\nError was:" + sqle.getMessage(), module);
-                                throw sqle;
-                            } finally {
-                                try {
-                                    if (stmt != null) stmt.close();
-                                } catch (SQLException sqle) {
-                                    Debug.logWarning(sqle, "Error closing statement in sequence util", module);
-                                }
-                                try {
-                                    if (connection != null) connection.close();
-                                } catch (SQLException sqle) {
-                                    Debug.logWarning(sqle, "Error closing connection in sequence util", module);
+                        // run an update with no changes to get a lock on the record
+                        if (stmt.executeUpdate(updateForLockStatement) <= 0) {
+                            Debug.logWarning("First select failed: will try to add new row, result set was empty for sequence [" + seqName + "] \nUsed SQL: " + sql + " \n ", module);
+                            sql = "INSERT INTO " + SequenceUtil.this.tableName + " (" + SequenceUtil.this.nameColName + ", " + SequenceUtil.this.idColName + ") VALUES ('" + this.seqName + "', " + startSeqId + ")";
+                            if (stmt.executeUpdate(sql) <= 0) {
+                                // insert failed: this means that another thread inserted the record; then retry to run an update with no changes to get a lock on the record
+                                if (stmt.executeUpdate(updateForLockStatement) <= 0) {
+                                    // This should never happen
+                                    throw new GenericEntityException("No rows changed when trying insert new sequence row with this SQL: " + sql);
                                 }
                             }
-                        } catch (Exception e) {
-                            String errMsg = "General error in getting a sequenced ID";
-                            Debug.logError(e, errMsg, module);
-                            try {
-                                TransactionUtil.rollback(beganTransaction, errMsg, e);
-                            } catch (GenericTransactionException gte2) {
-                                Debug.logError(gte2, "Unable to rollback transaction", module);
-                            }
-
-                            // error, break out of the loop to not try to continue forever
-                            break;
-                        } finally {
-                            try {
-                                TransactionUtil.commit(beganTransaction);
-                            } catch (GenericTransactionException gte) {
-                                Debug.logError(gte, "Unable to commit sequence increment transaction, continuing anyway though", module);
-                            }
                         }
-                    } catch (GenericTransactionException e) {
-                        Debug.logError(e, "System Error suspending transaction in sequence util", module);
+                        // select the record (now locked) to get the curSeqId
+                        rs = stmt.executeQuery(selectSequenceStatement);
+                        boolean gotVal = false;
+                        if (rs.next()) {
+                            curSeqId = rs.getLong(SequenceUtil.this.idColName);
+                            gotVal = true;
+                        }
+                        rs.close();
+                        if (!gotVal) {
+                            throw new GenericEntityException("Failed to find the sequence record: " + sql);
+                        }
+                        // increment the sequence
+                        sql = "UPDATE " + SequenceUtil.this.tableName + " SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + "+" + bankSize + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
+                        if (stmt.executeUpdate(sql) <= 0) {
+                            throw new GenericEntityException("Update failed, no rows changes for seqName: " + seqName);
+                        }
+
+                        TransactionUtil.commit(beganTransaction);
+
+                    } catch (SQLException sqle) {
+                        Debug.logWarning(sqle, "SQL Exception:" + sqle.getMessage(), module);
+                        throw sqle;
                     } finally {
-                        if (suspendedTransaction != null) {
-                            try {
-                                TransactionUtil.resume(suspendedTransaction);
-                            } catch (GenericTransactionException e) {
-                                Debug.logError(e, "Error resuming suspended transaction in sequence util", module);
-                            }
+                        try {
+                            if (stmt != null) stmt.close();
+                        } catch (SQLException sqle) {
+                            Debug.logWarning(sqle, "Error closing statement in sequence util", module);
+                        }
+                        try {
+                            if (connection != null) connection.close();
+                        } catch (SQLException sqle) {
+                            Debug.logWarning(sqle, "Error closing connection in sequence util", module);
                         }
                     }
-                }
-
-                if (val1 + bankSize != val2) {
-                    if (numTries >= maxTries) {
-                        String errMsg = "[SequenceUtil.SequenceBank.fillBank] maxTries (" + maxTries + ") reached for seqName [" + this.seqName + "], giving up.";
-                        Debug.logError(errMsg, module);
-                        return;
+                } catch (Exception e) {
+                    // reset the sequence fields and return (note: it would be better to throw an exception)
+                    curSeqId = 0;
+                    maxSeqId = 0;
+                    String errMsg = "General error in getting a sequenced ID";
+                    Debug.logError(e, errMsg, module);
+                    try {
+                        TransactionUtil.rollback(beganTransaction, errMsg, e);
+                    } catch (GenericTransactionException gte2) {
+                        Debug.logError(gte2, "Unable to rollback transaction", module);
                     }
-
-                    // collision happened, wait a bounded random amount of time then continue
-                    long waitTime = (long) (Math.random() * (maxWaitMillis - minWaitMillis) + minWaitMillis);
-
-                    Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Collision found for seqName [" + seqName + "], val1=" + val1 + ", val2=" + val2 + ", val1+bankSize=" + (val1 + bankSize) + ", bankSize=" + bankSize + ", waitTime=" + waitTime, module);
-
+                    return;
+                }
+            } catch (GenericTransactionException e) {
+                Debug.logError(e, "System Error suspending transaction in sequence util", module);
+                // reset the sequence fields and return (note: it would be better to throw an exception)
+                curSeqId = 0;
+                maxSeqId = 0;
+                return;
+            } finally {
+                if (suspendedTransaction != null) {
                     try {
-                        // using the Thread.sleep to more reliably lock this thread: this.wait(waitTime);
-                        java.lang.Thread.sleep(waitTime);
-                    } catch (Exception e) {
-                        Debug.logWarning(e, "Error waiting in sequence util", module);
+                        TransactionUtil.resume(suspendedTransaction);
+                    } catch (GenericTransactionException e) {
+                        Debug.logError(e, "Error resuming suspended transaction in sequence util", module);
+                        // reset the sequence fields and return (note: it would be better to throw an exception)
+                        curSeqId = 0;
+                        maxSeqId = 0;
                         return;
                     }
                 }
-
-                numTries++;
             }
 
-            curSeqId = val1;
-            maxSeqId = val2;
+            maxSeqId = curSeqId + bankSize;
             if (Debug.infoOn()) Debug.logInfo("Got bank of sequenced IDs for [" + this.seqName + "]; curSeqId=" + curSeqId + ", maxSeqId=" + maxSeqId + ", bankSize=" + bankSize, module);
-            //Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Ending fillBank Thread Name is: " + Thread.currentThread().getName() + ":" + Thread.currentThread().toString(), module);
         }
     }
 }
+

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml Mon Sep  1 07:29:23 2014
@@ -24,119 +24,139 @@
         <value xml:lang="fr">Impossible d'effectuer la synchro (push) avec un configuration en mode esclave seulement (pull only).</value>
         <value xml:lang="it">Non è possibile fare Entity Sync Push perchè entitySyncId [] è impostato in Pull Only.</value>
         <value xml:lang="zh">无法实现实体同步推送因为实体同步标识(entitySyncId [])被设置为了只能提取。</value>
+        <value xml:lang="zh_TW">無法進行個體同步推送因為個體同步代號(entitySyncId [])被設定為 Pull Only。</value>
     </property>
     <property key="EntityExtCannotFindDelegator">
         <value xml:lang="en">Could not find delegator with specified name ${overrideDelegatorName}</value>
         <value xml:lang="fr">Le delegateur avec le nom ${overrideDelegatorName} n'a pu être trouvé</value>
         <value xml:lang="it">Could not find delegator with specified name ${overrideDelegatorName}</value>
         <value xml:lang="zh">无法找到名称为${overrideDelegatorName}的代理</value>
+        <value xml:lang="zh_TW">無法找到名稱為${overrideDelegatorName}的代理</value>
     </property>
     <property key="EntityExtEntitySyncXMLDocumentIsNotValid">
         <value xml:lang="en">EntitySync XML document ${fileName} is not valid!</value>
         <value xml:lang="fr">L'EntitySync XML document ${fileName} n'est pas valide !</value>
         <value xml:lang="it">EntitySync XML documento ${fileName} non è valido!</value>
         <value xml:lang="zh">实体同步(EntitySync)XML文档${fileName}无效!</value>
+        <value xml:lang="zh_TW">個体同步(EntitySync)XML文件${fileName}無效!</value>
     </property>
     <property key="EntityExtErrorCallingRemotePull">
         <value xml:lang="en">Error calling remote pull and report EntitySync service with name: ${remotePullAndReportEntitySyncDataName}</value>
         <value xml:lang="fr">Erreur lors de l'appel de l'esclave (remote pull) et de son rapport sur le service nommé : ${remotePullAndReportEntitySyncDataName}</value>
         <value xml:lang="it">Errore durante la chiamata del pull remoto e report EntitySync servizio con il nome: ${remotePullAndReportEntitySyncDataName}</value>
         <value xml:lang="zh">调用远程提取和报表的实体同步(EntitySync)服务时出错,服务名称:${remotePullAndReportEntitySyncDataName}</value>
+        <value xml:lang="zh_TW">呼叫遠程提取和報表的個体同步(EntitySync)服務時出錯,服務名稱:${remotePullAndReportEntitySyncDataName}</value>
     </property>
     <property key="EntityExtErrorCallingService">
         <value xml:lang="en">Error calling service to store data locally</value>
         <value xml:lang="fr">Erreur d'appel du service de mise à jour des données locales</value>
         <value xml:lang="it">Errore durante la chiamata del servizio di registrazione locale dei dati</value>
         <value xml:lang="zh">调用服务来本地存储数据时出错</value>
+        <value xml:lang="zh_TW">呼叫服務來本地存儲資料時出錯</value>
     </property>
     <property key="EntityExtErrorCleaningEntitySyncRemove">
         <value xml:lang="en">Error cleaning out EntitySyncRemove info: ${errorString}</value>
         <value xml:lang="fr">Erreur lors du nettoyage des EntitySyncRemove, plus d'informations : ${errorString}</value>
         <value xml:lang="it">Error cleaning out EntitySyncRemove info: ${errorString}</value>
         <value xml:lang="zh">清除实体同步删除(EntitySyncRemove)信息时出错:${errorString}</value>
+        <value xml:lang="zh_TW">清除個體同步删除(EntitySyncRemove)訊息時出錯:${errorString}</value>
     </property>
     <property key="EntityExtErrorGettingListOfEntityInGroup">
         <value xml:lang="en">Error getting list of entities in group: ${errorString}</value>
         <value xml:lang="fr">Erreur lors de la récupération des entités du group : ${errorString}</value>
         <value xml:lang="it">Errore ad ottenere la lista delle entità nel gruppo: ${errorString}</value>
         <value xml:lang="zh">获取分组中的实体列表时出错:${errorString}</value>
+        <value xml:lang="zh_TW">取得分组中的個體列表時出錯:${errorString}</value>
     </property>
     <property key="EntityExtErrorUnwrappingRecords">
         <value xml:lang="en">Error unwrapping ByteWrapper records: ${errorString}</value>
         <value xml:lang="fr">Erreur lors de l"ouverture du flux des enregistrements : ${errorString}</value>
         <value xml:lang="it">Errore unwrapping ByteWrapper records: ${errorString}</value>
         <value xml:lang="zh">解析ByteWrapper记录时出错:${errorString}</value>
+        <value xml:lang="zh_TW">解析ByteWrapper記錄時出錯:${errorString}</value>
     </property>
     <property key="EntityExtErrorSavingEntitySyncData">
         <value xml:lang="en">Error saving Entity Sync Data for entitySyncId ${entitySyncId}: ${errorString}</value>
         <value xml:lang="fr">Erreur lors de la synchro de donnée pour la référence ${entitySyncId}: ${errorString}</value>
         <value xml:lang="it">Errore durante il salvataggio Entity Sync Data per entitySyncId ${entitySyncId}: ${errorString}</value>
         <value xml:lang="zh">为实体同步标识(entitySyncId)${entitySyncId}保存实体同步数据时出错:${errorString}</value>
+        <value xml:lang="zh_TW">為個體同步代號(entitySyncId)${entitySyncId}保存個體同步資料時出錯:${errorString}</value>
     </property>
     <property key="EntityExtExceptionSavingEntitySyncData">
         <value xml:lang="en">Exception saving Entity Sync Data for entitySyncId ${entitySyncId}: ${errorString}</value>
         <value xml:lang="fr">Exception lors de l'enregistrement de la synchro de donnée pour la référence ${entitySyncId}: ${errorString}</value>
         <value xml:lang="it">Eccezione durante il salvataggio Entity Sync Data per entitySyncId ${entitySyncId}: ${errorString}</value>
         <value xml:lang="zh">为实体同步标识(entitySyncId)${entitySyncId}保存实体同步数据时出现意外:${errorString}</value>
+        <value xml:lang="zh_TW">為個體同步代號(entitySyncId)${entitySyncId}保存個體同步資料時出現異常:${errorString}</value>
     </property>
     <property key="EntityExtFileNotFound">
         <value xml:lang="en">File not found: ${fileName}</value>
         <value xml:lang="fr">Fichier ${fileName} non trouvé</value>
         <value xml:lang="it">File non trovato: ${fileName}</value>
         <value xml:lang="zh">没有找到文件:${fileName}</value>
+        <value xml:lang="zh_TW">没有找到文件:${fileName}</value>
     </property>
     <property key="EntityExtNoFileAvailableInTheRootDirectory">
         <value xml:lang="en">No files available for reading in this root directory: ${rootDirectory}</value>
         <value xml:lang="fr">Aucun fichier disponible pour la lecture dans le répertoire ${rootDirectory}</value>
         <value xml:lang="it">Nessun file disponibile da leggere nella directory principale: ${rootDirectory}</value>
         <value xml:lang="zh">这个根目录下没有可读的文件:${rootDirectory}</value>
+        <value xml:lang="zh_TW">這個根目錄下没有可讀的文件:${rootDirectory}</value>
     </property>
     <property key="EntityExtOfflineXMLFileNotFound">
         <value xml:lang="en">Offline EntitySync XML file not found (${fileName})</value>
         <value xml:lang="fr">Le fichier hors ligne (${fileName}) n'a pas été trouvé</value>
         <value xml:lang="it">File Offline EntitySync XML non trovato (${fileName})</value>
         <value xml:lang="zh">没有找到离线实体同步(EntitySync)XML文件(${fileName})</value>
+        <value xml:lang="zh_TW">没有找到離線個體同步(EntitySync)XML文件(${fileName})</value>
     </property>
     <property key="EntityExtProblemReadingFile">
         <value xml:lang="en">Problem reading file: ${fileName}</value>
         <value xml:lang="fr">Problème lors de la lecture du fichier ${fileName}</value>
         <value xml:lang="it">Problema durante la lettura del file: ${fileName}</value>
         <value xml:lang="zh">读文件时出问题:${fileName}</value>
+        <value xml:lang="zh_TW">讀文件時出問題:${fileName}</value>
     </property>
     <property key="EntityExtRootDirectoryDoesNotExists">
         <value xml:lang="en">Root directory does not exist or is not readable.</value>
         <value xml:lang="fr">Le répertoire racine n'existe pas ou n'est pas lisible</value>
         <value xml:lang="it">Directory principale non esiste o non è leggibile.</value>
         <value xml:lang="zh">根目录不存在或不可读。</value>
+        <value xml:lang="zh_TW">根目錄不存在或不可讀。</value>
     </property>
     <property key="EntityExtServicePermissionNotGranted">
         <value xml:lang="en">You do not have permission to run this service.</value>
         <value xml:lang="fr">Vous n'avez pas la permission d'exécuter ce service</value>
         <value xml:lang="it">Tu non hai il permesso di eseguire questo servizio.</value>
         <value xml:lang="zh">你没有权限运行这个服务。</value>
+        <value xml:lang="zh_TW">你没有權限執行這個服務。</value>
     </property>
     <property key="EntityExtThisServiceIsNotYetImplemented">
         <value xml:lang="en">This service is not implemented yet.</value>
         <value xml:lang="fr">Ce service n'est pas implémenté actuellement</value>
         <value xml:lang="it">Questo servizio non è ancora implementato.</value>
         <value xml:lang="zh">这个服务尚未实现。</value>
+        <value xml:lang="zh_TW">這個服務尚未實作。</value>
     </property>
     <property key="EntityExtUnableToLoadXMLDocument">
         <value xml:lang="en">Unable to load EntitySync XML ${entitySyncId} - Problem at '${startTime}' Error: ${errorString}</value>
         <value xml:lang="fr">Impossible de charger l'EntitySync XML ${entitySyncId} - Problème à '${startTime}' , avec l'erreur suivante : ${errorString}</value>
         <value xml:lang="it">Non è possibile caricare il EntitySync XML ${entitySyncId} - Problema alle '${startTime}' Errore: ${errorString}</value>
         <value xml:lang="zh">无法载入实体同步(EntitySync)XML ${entitySyncId} - 问题发生在'${startTime}',错误:${errorString}</value>
+        <value xml:lang="zh_TW">無法載入個體同步(EntitySync)XML ${entitySyncId} - 問題發生在'${startTime}',錯誤:${errorString}</value>
     </property>
     <property key="EntityExtUnableToLocateRootDirectory">
         <value xml:lang="en">Unable to locate root directory ${rootDirectory}</value>
         <value xml:lang="fr">Impossible de localiser le répertoire racine ${rootDirectory}</value>
         <value xml:lang="it">Non è possibile localizzare la directory principale ${rootDirectory}</value>
         <value xml:lang="zh">无法定位根目录${rootDirectory}</value>
+        <value xml:lang="zh_TW">無法定位根目錄${rootDirectory}</value>
     </property>
     <property key="EntityExtUnableToLocateRootDirectoryURI">
         <value xml:lang="en">Unable to get root directory URI</value>
         <value xml:lang="fr">Impossible de récupérer le répertoire racine</value>
         <value xml:lang="it">Non è possibile localizzare la directory principale URI</value>
         <value xml:lang="zh">不能得到根目录URI</value>
+        <value xml:lang="zh_TW">不能取得根目錄URI</value>
     </property>
 </resource>

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml Mon Sep  1 07:29:23 2014
@@ -26,6 +26,7 @@
         <value xml:lang="ja">エラー:</value>
         <value xml:lang="nl">Fout: </value>    
         <value xml:lang="zh">错误:</value>
+        <value xml:lang="zh_TW">錯誤:</value>
     </property>
     <property key="check.error.suffix">
         <value xml:lang="en">&#160;</value>
@@ -58,6 +59,7 @@
         <value xml:lang="ja">エラー:</value>
         <value xml:lang="nl">Fout: </value>    
         <value xml:lang="zh">错误:</value>
+        <value xml:lang="zh_TW">錯誤:</value>
     </property>
     <property key="service.error.suffix">
         <value xml:lang="en">&#160;</value>

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml Mon Sep  1 07:29:23 2014
@@ -43,13 +43,14 @@
         <value xml:lang="ru">Ошибка выполнения simple-method</value>
         <value xml:lang="th">Error : ผิดพลาด ! ในการรัน simple-method</value>
         <value xml:lang="zh">运行这个简单方法时出错</value>
-        <value xml:lang="zh_TW">運行這個簡單方法時出錯</value>
+        <value xml:lang="zh_TW">執行這個簡單方法時出錯</value>
     </property>
     <property key="simpleMethod.error_show_service_name">
         <value xml:lang="en">calling service ${serviceName} in ${methodName}</value>
         <value xml:lang="it">eseguendo il servizio ${serviceName} in ${methodName}</value>
         <value xml:lang="ja">サービス ${serviceName} を ${methodName} で呼び出し</value>
         <value xml:lang="zh">在${methodName}中调用服务${serviceName} </value>
+        <value xml:lang="zh_TW">在${methodName}中呼叫服務${serviceName} </value>
     </property>
     <property key="simpleMethod.must_logged_process">
         <value xml:lang="de">Sie müssen am System angemeldet sein um [${shortDescription}] auszuführen</value>
@@ -62,6 +63,6 @@
         <value xml:lang="ru">Для завершения процесса ${shortDescription} вы должны зарегистрироваться в системе</value>
         <value xml:lang="th">คุณต้องทำการเข้าสู่ระบบในการประมวลผล  [${shortDescription}] ให้เสร็จสมบูรณ์</value>
         <value xml:lang="zh">你必须登录才能完成${shortDescription}步骤</value>
-        <value xml:lang="zh_TW">你必須登錄才能完成${shortDescription}步驟</value>
+        <value xml:lang="zh_TW">你必須登入才能完成${shortDescription}步驟</value>
     </property>
 </resource>

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java Mon Sep  1 07:29:23 2014
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -84,7 +85,7 @@ public final class SimpleMethod extends
     private static final String[] DEPRECATED_ATTRIBUTES = {"parameter-map-name", "locale-name", "delegator-name", "security-name", "dispatcher-name", "user-login-name"};
     private static final Map<String, MethodOperation.Factory<MethodOperation>> methodOperationFactories;
     private static final UtilCache<String, Map<String, SimpleMethod>> simpleMethodsDirectCache = UtilCache.createUtilCache("minilang.SimpleMethodsDirect", 0, 0);
-    private static final UtilCache<String, Map<String, SimpleMethod>> simpleMethodsResourceCache = UtilCache.createUtilCache("minilang.SimpleMethodsResource", 0, 0);
+    private static final UtilCache<String, SimpleMethod> simpleMethodsResourceCache = UtilCache.createUtilCache("minilang.SimpleMethodsResource", 0, 0);
 
     static {
         Map<String, MethodOperation.Factory<MethodOperation>> mapFactories = new HashMap<String, MethodOperation.Factory<MethodOperation>>();
@@ -134,7 +135,7 @@ public final class SimpleMethod extends
     }
 
     private static Map<String, SimpleMethod> getAllSimpleMethods(URL xmlURL) throws MiniLangException {
-        Map<String, SimpleMethod> simpleMethods = new HashMap<String, SimpleMethod>();
+        Map<String, SimpleMethod> simpleMethods = new LinkedHashMap<String, SimpleMethod>();
         Document document = null;
         try {
             document = UtilXml.readXmlDocument(xmlURL, true, true);
@@ -160,14 +161,31 @@ public final class SimpleMethod extends
 
     public static SimpleMethod getSimpleMethod(String xmlResource, String methodName, ClassLoader loader) throws MiniLangException {
         Assert.notNull("methodName", methodName);
-        Map<String, SimpleMethod> simpleMethods = getSimpleMethods(xmlResource, loader);
-        return simpleMethods.get(methodName);
+        String key = xmlResource.concat("#").concat(methodName);
+        SimpleMethod method = simpleMethodsResourceCache.get(key);
+        if (method == null) {
+            Map<String, SimpleMethod> simpleMethods = getSimpleMethods(xmlResource, loader);
+            for (Map.Entry<String, SimpleMethod> entry : simpleMethods.entrySet()) {
+                String putKey = xmlResource.concat("#").concat(entry.getKey());
+                simpleMethodsResourceCache.putIfAbsent(putKey, entry.getValue());
+            }
+        }
+        return simpleMethodsResourceCache.get(key);
     }
 
     public static SimpleMethod getSimpleMethod(URL xmlUrl, String methodName) throws MiniLangException {
         Assert.notNull("methodName", methodName);
-        Map<String, SimpleMethod> simpleMethods = getSimpleMethods(xmlUrl);
-        return simpleMethods.get(methodName);
+        String xmlResource = xmlUrl.toString();
+        String key = xmlResource.concat("#").concat(methodName);
+        SimpleMethod method = simpleMethodsResourceCache.get(key);
+        if (method == null) {
+            Map<String, SimpleMethod> simpleMethods = getAllSimpleMethods(xmlUrl);
+            for (Map.Entry<String, SimpleMethod> entry : simpleMethods.entrySet()) {
+                String putKey = xmlResource.concat("#").concat(entry.getKey());
+                simpleMethodsResourceCache.putIfAbsent(putKey, entry.getValue());
+            }
+        }
+        return simpleMethodsResourceCache.get(key);
     }
 
     private static Map<String, SimpleMethod> getSimpleMethods(String xmlResource, ClassLoader loader) throws MiniLangException {
@@ -181,43 +199,23 @@ public final class SimpleMethod extends
         if (xmlURL == null) {
             throw new MiniLangException("Could not find SimpleMethod XML document in resource: " + xmlResource);
         }
-        return getSimpleMethods(xmlURL);
-    }
-
-    private static Map<String, SimpleMethod> getSimpleMethods(URL xmlURL) throws MiniLangException {
-        Assert.notNull("xmlURL", xmlURL);
-        String cacheKey = xmlURL.toString();
-        Map<String, SimpleMethod> simpleMethods = simpleMethodsResourceCache.get(cacheKey);
-        if (simpleMethods == null) {
-            simpleMethods = getAllSimpleMethods(xmlURL);
-            simpleMethods = simpleMethodsResourceCache.putIfAbsentAndGet(cacheKey, simpleMethods);
-        }
-        return simpleMethods;
+        return getAllSimpleMethods(xmlURL);
     }
 
+    /**
+     * Returns a List of <code>SimpleMethod</code> objects compiled from <code>xmlResource</code>.
+     * The ordering in the List is the same as the XML file.
+     * <p>This method is used by unit test framework to run tests in the order they appear in the XML file.
+     * Method caching is bypassed since the methods are executed only once.</p>
+     *
+     * @param xmlResource
+     * @param loader
+     * @return
+     * @throws MiniLangException
+     */
     public static List<SimpleMethod> getSimpleMethodsList(String xmlResource, ClassLoader loader) throws MiniLangException {
-        Assert.notNull("xmlResource", xmlResource);
-        List<SimpleMethod> simpleMethods = new ArrayList<SimpleMethod>();
-        // Let the standard Map returning method take care of caching and compilation
         Map<String, SimpleMethod> simpleMethodMap = getSimpleMethods(xmlResource, loader);
-        // Load and traverse the document again to get a correctly ordered list of methods
-        URL xmlURL = null;
-        try {
-            xmlURL = FlexibleLocation.resolveLocation(xmlResource, loader);
-        } catch (MalformedURLException e) {
-            throw new MiniLangException("Could not find SimpleMethod XML document in resource: " + xmlResource + ": ", e);
-        }
-        Document document = null;
-        try {
-            document = UtilXml.readXmlDocument(xmlURL, MiniLangValidate.validationOn(), true);
-        } catch (Exception e) {
-            throw new MiniLangException("Could not read SimpleMethod XML document [" + xmlURL + "]: ", e);
-        }
-        Element rootElement = document.getDocumentElement();
-        for (Element simpleMethodElement : UtilXml.childElementList(rootElement, "simple-method")) {
-            simpleMethods.add(simpleMethodMap.get(simpleMethodElement.getAttribute("method-name")));
-        }
-        return simpleMethods;
+        return new ArrayList<SimpleMethod>(simpleMethodMap.values());
     }
 
     public static List<MethodOperation> readOperations(Element simpleMethodElement, SimpleMethod simpleMethod) throws MiniLangException {

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java Mon Sep  1 07:29:23 2014
@@ -169,10 +169,6 @@ public final class CallSimpleMethod exte
         return this.methodName;
     }
 
-    public SimpleMethod getSimpleMethodToCall(ClassLoader loader) throws MiniLangException {
-        return SimpleMethod.getSimpleMethod(xmlResource, methodName, loader);
-    }
-
     public String getXmlResource() {
         return this.xmlResource;
     }

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml Mon Sep  1 07:29:23 2014
@@ -3,17 +3,21 @@
     <property key="@component-resource-name@Application">
         <value xml:lang="en">@component-resource-name@ Application</value>
         <value xml:lang="zh">@component-resource-name@应用程序</value>
+        <value xml:lang="zh_TW">@component-resource-name@應用程序</value>
     </property>
     <property key="@component-resource-name@CompanyName">
         <value xml:lang="en">OFBiz: @component-resource-name@</value>
+        <value xml:lang="zh_TW">OFBiz: @component-resource-name@</value>
     </property>
     <property key="@component-resource-name@CompanySubtitle">
         <value xml:lang="en">Part of the Open For Business Family of Open Source Software</value>
         <value xml:lang="it">Un modulo della famiglia di software open source Open For Business</value>
         <value xml:lang="zh">开源软件OFBiz的组成部分</value>
+        <value xml:lang="zh_TW">開源軟體OFBiz的组成部分</value>
     </property>
     <property key="@component-resource-name@ViewPermissionError">
         <value xml:lang="en">You are not allowed to view this page.</value>
         <value xml:lang="zh">不允许你浏览这个页面。</value>
+        <value xml:lang="zh_TW">不允許您瀏覽這個頁面。</value>
     </property>
 </resource>
\ No newline at end of file