From 9d5f9f291b1df442816ba7b8a1b4be9e14e51c05 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 20 Jun 2012 15:26:08 +0800 Subject: [PATCH] IDEMPIERE-312 Performance: Use JDK ThreadPool API for dynamically created thread --- .../src/org/adempiere/base/BaseActivator.java | 2 + .../src/org/compiere/Adempiere.java | 63 +++++++++++++++++++ .../src/org/compiere/model/GridTable.java | 28 +++++---- .../src/org/compiere/model/MLookup.java | 60 +++++++++++------- .../adempiere/webui/apps/ProcessDialog.java | 12 ++-- .../webui/apps/ProcessModalDialog.java | 33 +++++----- 6 files changed, 144 insertions(+), 54 deletions(-) diff --git a/org.adempiere.base/src/org/adempiere/base/BaseActivator.java b/org.adempiere.base/src/org/adempiere/base/BaseActivator.java index 442c943b70..895af6e276 100644 --- a/org.adempiere.base/src/org/adempiere/base/BaseActivator.java +++ b/org.adempiere.base/src/org/adempiere/base/BaseActivator.java @@ -20,6 +20,7 @@ package org.adempiere.base; import org.adempiere.base.equinox.StackTraceCommand; +import org.compiere.Adempiere; import org.eclipse.osgi.framework.console.CommandProvider; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; @@ -53,6 +54,7 @@ public class BaseActivator implements BundleActivator { @Override public void stop(BundleContext context) throws Exception { bundleContext = null; + Adempiere.stop(); } public static BundleContext getBundleContext() { diff --git a/org.adempiere.base/src/org/compiere/Adempiere.java b/org.adempiere.base/src/org/compiere/Adempiere.java index 59ed37c18c..0cd15dc3ef 100644 --- a/org.adempiere.base/src/org/compiere/Adempiere.java +++ b/org.adempiere.base/src/org/compiere/Adempiere.java @@ -23,6 +23,9 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Properties; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.jnlp.BasicService; @@ -103,6 +106,9 @@ public final class Adempiere /** Logging */ private static CLogger log = null; + + /** Thread pool **/ + private static ThreadPoolExecutor threadPoolExecutor = null; static { ClassLoader loader = Adempiere.class.getClassLoader(); @@ -504,6 +510,22 @@ public final class Adempiere if (isClient && Ini.isPropertyBool(Ini.P_TRACEFILE)) CLogMgt.addHandler(new CLogFile(Ini.findAdempiereHome(), true, isClient)); + //setup specific log level + Properties properties = Ini.getProperties(); + for(Object key : properties.keySet()) + { + if (key instanceof String) + { + String s = (String)key; + if (s.endsWith("."+Ini.P_TRACELEVEL)) + { + String level = properties.getProperty(s); + s = s.substring(0, s.length() - ("."+Ini.P_TRACELEVEL).length()); + CLogMgt.setLevel(s, level); + } + } + } + // Set UI if (isClient) { @@ -514,12 +536,43 @@ public final class Adempiere // Set Default Database Connection from Ini DB.setDBTarget(CConnection.get(getCodeBaseHost())); + createThreadPool(); + if (isClient) // don't test connection return false; // need to call return startupEnvironment(isClient); } // startup + private static void createThreadPool() { + int min = 20; + int max = 200; + Properties properties = Ini.getProperties(); + String maxSize = properties.getProperty("MaxThreadPoolSize"); + String minSize = properties.getProperty("MinThreadPoolSize"); + if (maxSize != null) { + try { + max = Integer.parseInt(maxSize); + } catch (Exception e) {} + } + if (minSize != null) { + try { + min = Integer.parseInt(minSize); + } catch (Exception e) {} + } + if (max < min) { + max = min; + } + if (max <= 0) { + max = 200; + } + if (min < 0) { + min = 20; + } + // start thread pool + threadPoolExecutor = new ThreadPoolExecutor(min, max, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); + } + /** * Startup Adempiere Environment. * Automatically called for Server connections @@ -591,4 +644,14 @@ public final class Adempiere public static URL getResource(String name) { return Core.getResourceFinder().getResource(name); } + + public static synchronized void stop() { + if (threadPoolExecutor != null) { + threadPoolExecutor.shutdown(); + } + } + + public static ThreadPoolExecutor getThreadPoolExecutor() { + return threadPoolExecutor; + } } // Adempiere diff --git a/org.adempiere.base/src/org/compiere/model/GridTable.java b/org.adempiere.base/src/org/compiere/model/GridTable.java index cda332be52..85d3a30154 100644 --- a/org.adempiere.base/src/org/compiere/model/GridTable.java +++ b/org.adempiere.base/src/org/compiere/model/GridTable.java @@ -35,12 +35,14 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.concurrent.Future; import java.util.logging.Level; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; import org.adempiere.exceptions.DBException; +import org.compiere.Adempiere; import org.compiere.util.CLogMgt; import org.compiere.util.CLogger; import org.compiere.util.DB; @@ -214,7 +216,7 @@ public class GridTable extends AbstractTableModel /** Vetoable Change Bean support */ private VetoableChangeSupport m_vetoableChangeSupport = new VetoableChangeSupport(this); - private Thread m_loaderThread; + private Future m_loaderFuture; /** Property of Vetoable Bean support "RowChange" */ public static final String PROPERTY = "MTable-RowSave"; @@ -618,8 +620,7 @@ public class GridTable extends AbstractTableModel m_loader.run(); else { - m_loaderThread = new Thread(m_loader, "TLoader"); - m_loaderThread.start(); + m_loaderFuture = Adempiere.getThreadPoolExecutor().submit(m_loader); } } else @@ -656,17 +657,17 @@ public class GridTable extends AbstractTableModel public void loadComplete() { // Wait for loader - if (m_loaderThread != null) + if (m_loaderFuture != null) { - if (m_loaderThread.isAlive()) + if (!m_loaderFuture.isDone()) { try { - m_loaderThread.join(); + m_loaderFuture.get(); } - catch (InterruptedException ie) + catch (Exception ie) { - log.log(Level.SEVERE, "Join interrupted", ie); + log.log(Level.SEVERE, "Interrupted", ie); } } } @@ -684,7 +685,7 @@ public class GridTable extends AbstractTableModel */ public boolean isLoading() { - if (m_loaderThread != null && m_loaderThread.isAlive()) + if (m_loaderFuture != null && !m_loaderFuture.isDone()) return true; return false; } // isLoading @@ -723,16 +724,17 @@ public class GridTable extends AbstractTableModel } // Stop loader - while (m_loaderThread != null && m_loaderThread.isAlive()) + while (m_loaderFuture != null && !m_loaderFuture.isDone()) { log.fine("Interrupting Loader ..."); - m_loaderThread.interrupt(); + m_loaderFuture.cancel(true); try { Thread.sleep(200); // .2 second } catch (InterruptedException ie) {} + m_loaderFuture = null; } if (!m_inserting) @@ -787,7 +789,7 @@ public class GridTable extends AbstractTableModel m_rowData = null; m_oldValue = null; m_loader = null; - m_loaderThread = null; + m_loaderFuture = null; } // dispose /** @@ -985,7 +987,7 @@ public class GridTable extends AbstractTableModel // need to wait for data read into buffer int loops = 0; - while (row >= m_sort.size() && m_loaderThread != null && m_loaderThread.isAlive() && loops < 15) + while (row >= m_sort.size() && m_loaderFuture != null && !m_loaderFuture.isDone() && loops < 15) { log.fine("Waiting for loader row=" + row + ", size=" + m_sort.size()); try diff --git a/org.adempiere.base/src/org/compiere/model/MLookup.java b/org.adempiere.base/src/org/compiere/model/MLookup.java index 673ff020dd..c937d0541c 100644 --- a/org.adempiere.base/src/org/compiere/model/MLookup.java +++ b/org.adempiere.base/src/org/compiere/model/MLookup.java @@ -23,8 +23,10 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.concurrent.Future; import java.util.logging.Level; +import org.compiere.Adempiere; import org.compiere.util.CLogMgt; import org.compiere.util.DB; import org.compiere.util.DisplayType; @@ -67,8 +69,11 @@ public final class MLookup extends Lookup implements Serializable log.fine(m_info.KeyColumn); // load into local lookup, if already cached - if (MLookupCache.loadFromCache (m_info, m_lookup)) - return; + if (Ini.isClient()) + { + if (MLookupCache.loadFromCache (m_info, m_lookup)) + return; + } // Don't load Search or CreatedBy/UpdatedBy if (m_info.DisplayType == DisplayType.Search @@ -124,9 +129,10 @@ public final class MLookup extends Lookup implements Serializable { if (m_info != null) log.fine(m_info.KeyColumn + ": dispose"); - if (m_loader != null && m_loader.isAlive()) - m_loader.interrupt(); + if (m_loaderFuture != null && !m_loaderFuture.isDone()) + m_loaderFuture.cancel(true); m_loader = null; + m_loaderFuture = null; // if (m_lookup != null) m_lookup.clear(); @@ -145,17 +151,18 @@ public final class MLookup extends Lookup implements Serializable */ public void loadComplete() { - if (m_loader != null && m_loader.isAlive()) + if (m_loaderFuture != null && !m_loaderFuture.isDone()) { - try + try { - m_loader.join(); - m_loader = null; + m_loaderFuture.get(); } - catch (InterruptedException ie) + catch (Exception ie) { log.log(Level.SEVERE, m_info.KeyColumn + ": Interrupted", ie); - } + } + m_loader = null; + m_loaderFuture = null; } } // loadComplete @@ -188,7 +195,7 @@ public final class MLookup extends Lookup implements Serializable return retValue; // Not found and waiting for loader - if (m_loader != null && m_loader.isAlive()) + if (m_loaderFuture != null && !m_loaderFuture.isDone()) { log.finer((m_info.KeyColumn==null ? "ID="+m_info.Column_ID : m_info.KeyColumn) + ": waiting for Loader"); loadComplete(); @@ -332,7 +339,7 @@ public final class MLookup extends Lookup implements Serializable */ private ArrayList getData (boolean onlyValidated, boolean loadParent) { - if (m_loader != null && m_loader.isAlive()) + if (m_loaderFuture != null && !m_loaderFuture.isDone()) { log.fine((m_info.KeyColumn==null ? "ID="+m_info.Column_ID : m_info.KeyColumn) + ": waiting for Loader"); @@ -407,6 +414,7 @@ public final class MLookup extends Lookup implements Serializable private HashMap m_lookupDirect = null; /** Save last unsuccessful */ private Object m_directNullKey = null; + private Future m_loaderFuture; /** * Get Data Direct from Table. @@ -592,7 +600,7 @@ public final class MLookup extends Lookup implements Serializable log.fine(m_info.KeyColumn + ": start"); m_loader = new MLoader(); - m_loader.start(); + m_loaderFuture = Adempiere.getThreadPoolExecutor().submit(m_loader); loadComplete(); log.fine(m_info.KeyColumn + ": #" + m_lookup.size()); @@ -631,7 +639,7 @@ public final class MLookup extends Lookup implements Serializable /************************************************************************** * MLookup Loader */ - class MLoader extends Thread implements Serializable + class MLoader implements Serializable, Runnable { /** * @@ -643,9 +651,6 @@ public final class MLookup extends Lookup implements Serializable */ public MLoader() { - super("MLoader-" + m_info.KeyColumn); - // if (m_info.KeyColumn.indexOf("C_InvoiceLine_ID") != -1) - // log.info(m_info.KeyColumn); } // Loader private long m_startTime = System.currentTimeMillis(); @@ -656,7 +661,8 @@ public final class MLookup extends Lookup implements Serializable public void run() { long startTime = System.currentTimeMillis(); - MLookupCache.loadStart (m_info); + if (Ini.isClient()) + MLookupCache.loadStart (m_info); String sql = m_info.Query; // not validated @@ -697,7 +703,7 @@ public final class MLookup extends Lookup implements Serializable } } // check - if (isInterrupted()) + if (Thread.interrupted()) { log.log(Level.WARNING, m_info.KeyColumn + ": Loader interrupted"); return; @@ -727,11 +733,20 @@ public final class MLookup extends Lookup implements Serializable { if (rows++ > MAX_ROWS) { - log.warning(m_info.KeyColumn + ": Loader - Too many records"); + String s = m_info.KeyColumn + ": Loader - Too many records"; + if (m_info.Column_ID > 0) + { + MColumn mColumn = MColumn.get(m_info.ctx, m_info.Column_ID); + String column = mColumn.getColumnName(); + s = s + ", Column="+column; + String tableName = MTable.getTableName(m_info.ctx, mColumn.getAD_Table_ID()); + s = s + ", Table="+tableName; + } + log.warning(s); break; } // check for interrupted every 10 rows - if (rows % 20 == 0 && isInterrupted()) + if (rows % 20 == 0 && Thread.interrupted()) break; // load data @@ -773,7 +788,8 @@ public final class MLookup extends Lookup implements Serializable + " - ms=" + String.valueOf(System.currentTimeMillis()-m_startTime) + " (" + String.valueOf(System.currentTimeMillis()-startTime) + ")"); // if (m_allLoaded) - MLookupCache.loadEnd (m_info, m_lookup); + if (Ini.isClient()) + MLookupCache.loadEnd (m_info, m_lookup); } // run } // Loader diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java index 2cb3139c58..243155505e 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Properties; +import java.util.concurrent.Future; import java.util.logging.Level; import org.adempiere.util.IProcessMonitor; @@ -22,6 +23,7 @@ import org.adempiere.webui.process.WProcessInfo; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.window.FDialog; import org.adempiere.webui.window.SimplePDFViewer; +import org.compiere.Adempiere; import org.compiere.model.SystemIDs; import org.compiere.print.ReportEngine; import org.compiere.process.ProcessInfo; @@ -90,7 +92,7 @@ public class ProcessDialog extends Window implements EventListener, IProc private Center center; private North north; - + /** * Dialog to start a process/report * @param ctx @@ -197,7 +199,8 @@ public class ProcessDialog extends Window implements EventListener, IProc private boolean isParameterPage = true; private String initialMessage; private BusyDialog progressWindow; - private Thread thread; + @SuppressWarnings("unused") + private Future future; private ProcessDialogRunnable processDialogRunnable; private static final String ON_STATUS_UPDATE = "onStatusUpdate"; @@ -333,13 +336,12 @@ public class ProcessDialog extends Window implements EventListener, IProc p.put(AdempiereWebUI.ZK_DESKTOP_SESSION_KEY, desktop); processDialogRunnable = new ProcessDialogRunnable(p); - thread = new Thread(processDialogRunnable); - thread.start(); + future = Adempiere.getThreadPoolExecutor().submit(processDialogRunnable); } private void onComplete() { Env.getCtx().putAll(processDialogRunnable.getProperties()); - thread = null; + future = null; processDialogRunnable = null; unlockUI(m_pi); } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessModalDialog.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessModalDialog.java index 60b13f1b89..b9eb5e0e08 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessModalDialog.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessModalDialog.java @@ -20,6 +20,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; +import java.util.concurrent.Future; import java.util.logging.Level; import org.adempiere.util.IProcessMonitor; @@ -31,6 +32,7 @@ import org.adempiere.webui.component.Panel; import org.adempiere.webui.component.VerticalBox; import org.adempiere.webui.component.Window; import org.adempiere.webui.event.DialogEvents; +import org.compiere.Adempiere; import org.compiere.process.ProcessInfo; import org.compiere.util.CLogger; import org.compiere.util.DB; @@ -70,7 +72,7 @@ public class ProcessModalDialog extends Window implements EventListener, private static final long serialVersionUID = -7109707014309321369L; private boolean m_autoStart; private VerticalBox dialogBody; - + /** * @param aProcess * @param WindowNo @@ -84,7 +86,7 @@ public class ProcessModalDialog extends Window implements EventListener, m_WindowNo = WindowNo; m_pi = pi; m_autoStart = autoStart; - + log.info("Process=" + pi.getAD_Process_ID()); try { @@ -143,9 +145,11 @@ public class ProcessModalDialog extends Window implements EventListener, dialogBody.appendChild(div); centerPanel = new Panel(); dialogBody.appendChild(centerPanel); - div = new Div(); - div.setStyle("text-align: right"); +// div = new Div(); +// div.setStyle("text-align: right"); Hbox hbox = new Hbox(); + hbox.setWidth("100%"); + hbox.setStyle("margin-top: 10px"); Button btn = new Button("Ok"); LayoutUtils.addSclass("action-text-button", btn); btn.setId("Ok"); @@ -158,8 +162,9 @@ public class ProcessModalDialog extends Window implements EventListener, btn.addEventListener(Events.ON_CLICK, this); hbox.appendChild(btn); - div.appendChild(hbox); - dialogBody.appendChild(div); + hbox.setPack("end"); +// div.appendChild(hbox); + dialogBody.appendChild(hbox); this.appendChild(dialogBody); } @@ -185,7 +190,8 @@ public class ProcessModalDialog extends Window implements EventListener, private BusyDialog progressWindow; private boolean isLocked = false; private org.adempiere.webui.apps.ProcessModalDialog.ProcessDialogRunnable processDialogRunnable; - private Thread thread; + @SuppressWarnings("unused") + private Future future; /** * Set Visible @@ -317,14 +323,14 @@ public class ProcessModalDialog extends Window implements EventListener, * launch process */ private void startProcess() - { + { m_pi.setPrintPreview(true); if (m_processMonitor != null) { m_processMonitor.lockUI(m_pi); Clients.clearBusy(); } - + lockUI(m_pi); //use echo, otherwise lock ui wouldn't work @@ -361,8 +367,7 @@ public class ProcessModalDialog extends Window implements EventListener, p.put(AdempiereWebUI.ZK_DESKTOP_SESSION_KEY, desktop); processDialogRunnable = new ProcessDialogRunnable(p); - thread = new Thread(processDialogRunnable); - thread.start(); + future = Adempiere.getThreadPoolExecutor().submit(processDialogRunnable); } private void hideBusyDialog() { @@ -409,14 +414,14 @@ public class ProcessModalDialog extends Window implements EventListener, private void onComplete() { Env.getCtx().putAll(processDialogRunnable.getProperties()); - thread = null; + future = null; processDialogRunnable = null; - dispose(); + dispose(); if (m_processMonitor != null) { m_processMonitor.unlockUI(m_pi); } unlockUI(m_pi); - Events.sendEvent(this, new Event(ON_MODAL_CLOSE, this, null)); + Events.sendEvent(this, new Event(ON_MODAL_CLOSE, this, null)); } @Override