IDEMPIERE-312 Performance: Use JDK ThreadPool API for dynamically created thread

This commit is contained in:
Heng Sin Low 2012-06-20 15:26:08 +08:00
parent 15a8173f54
commit 9d5f9f291b
6 changed files with 144 additions and 54 deletions

View File

@ -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() {

View File

@ -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<Runnable>());
}
/**
* 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

View File

@ -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

View File

@ -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<Object> 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<Object,Object> 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

View File

@ -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<Event>, 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<Event>, 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<Event>, 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);
}

View File

@ -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<Event>,
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<Event>,
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<Event>,
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<Event>,
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<Event>,
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<Event>,
* 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<Event>,
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<Event>,
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