As we transition to the new Hyland Community, some post links may be temporarily disrupted as we finalize redirects. We apologize for the inconvenience and are working to resolve this as quickly as possible. Details.
package pac1; import java.util.ArrayList; import java.util.List; import javax.sql.DataSource; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngineConfiguration; import org.activiti.engine.RepositoryService; import org.activiti.engine.RuntimeService; import org.activiti.engine.repository.Deployment; import org.apache.commons.dbcp.BasicDataSource; public class Prog3 { public static void main(String[] args) throws Exception { ProcessEngine processEngine = null; BasicDataSource myDataSource = null; try { System.out.println("starting!"); int threadNumber = 15; int maxConnectionNumber = 3; myDataSource = new org.apache.commons.dbcp.BasicDataSource(); myDataSource.setDriverClassName("org.postgresql.Driver"); myDataSource.setUrl("jdbc:postgresql://127.0.0.1:54322/activiti"); myDataSource.setUsername("activiti"); myDataSource.setPassword("activiti"); myDataSource.setMaxActive(maxConnectionNumber); myDataSource.setMinIdle(0); System.out.println("max Active: " + myDataSource.getMaxActive()); DataSource ds = new HackedDataSource(myDataSource); ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration() .setDataSource(ds); processEngine = processEngineConfiguration.buildProcessEngine(); System.out.println("processEngine created OK!"); // Deploy the test process RepositoryService repositoryService = processEngine .getRepositoryService(); Deployment deployment = repositoryService.createDeployment() .addClasspathResource("tescior3.bpmn20.xml").deploy(); System.out.println("process deployed OK!"); final RuntimeService runtimeService = processEngine .getRuntimeService(); System.out.println("ok, got runtimeService"); // Start a number of process instances List<Thread> myThreads = new ArrayList<Thread>(); for (int i = 0; i < threadNumber; i++) { Thread thr = new Thread(new Runnable() { @Override public void run() { runtimeService.startProcessInstanceByKey("test3"); } }); thr.start(); myThreads.add(thr); } for (Thread t : myThreads) { t.join(); } System.out.println("ok, all threads joined"); } finally { if (processEngine != null) try { System.out.println("about to close process engine…"); processEngine.close(); System.out.println("process engine closed"); } catch (Exception e) { e.printStackTrace(); } if (myDataSource != null) try { System.out.println("about to close data source…"); myDataSource.close(); System.out.println("data source closed"); } catch (Exception e) { e.printStackTrace(); } } } }
<br>Thread A Thread B<br>getConnection -> OK!<br> getConnection -> OK!<br>getConnection -> wait…<br> getConnection -> wait…<br>…now both threads are just stuck forever…<br>
What if you change your pool with a datasource pooling solutiuon such as DBCP? The iBatis default connection pool is fairly simple implemented, last time I went through the code.
<br> myDataSource = new org.apache.commons.dbcp.BasicDataSource();<br> myDataSource.setDriverClassName("org.postgresql.Driver");<br> myDataSource.setUrl("jdbc:smileytongue:ostgresql://127.0.0.1:54322/activiti");<br> myDataSource.setUsername("activiti");<br> myDataSource.setPassword("activiti");<br> myDataSource.setMaxActive(maxConnectionNumber);<br> myDataSource.setMinIdle(0);<br> System.out.println("max Active: " + myDataSource.getMaxActive());<br><br> DataSource ds = new HackedDataSource(myDataSource);<br> ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration<br> .createStandaloneProcessEngineConfiguration()<br> .setDataSource(ds);<br><br> processEngine = processEngineConfiguration.buildProcessEngine();<br> System.out.println("processEngine created OK!");<br>
<br>public class HackedConnection implements Connection {<br><br> private Connection conn;<br><br> public HackedConnection(Connection conn) {<br> this.conn = conn;<br> }<br><br> public void close() throws SQLException {<br> String name = Thread.currentThread().getName();<br> System.out.println(" –Connection.close called for thread " + name<br> + ".");<br> HackerDatabase.customerLeaves(name);<br> conn.close();<br> System.out.println(" –Connection.close finishes for thread " + name<br> + ".");<br> }<br>..delegate methods…<br>
<br>public class HackedDataSource implements DataSource {<br><br> private DataSource dataSource;<br> private TreeSet<String> customers;<br><br> public HackedDataSource(DataSource dataSource) {<br> this.dataSource = dataSource;<br> this.customers = new TreeSet<String>();<br> }<br><br> public Connection getConnection() throws SQLException {<br> String name = Thread.currentThread().getName();<br> System.out.println(" –getConnection called for thread " + name + ".");<br> synchronized (customers) {<br> if (!"main".equals(name)) {<br> if (customers.contains(name))<br> System.out.println(" —-hey!");<br> customers.add(name);<br> }<br> }<br><br> HackerDatabase.customerAsking(name);<br> Connection conn = dataSource.getConnection();<br> HackerDatabase.customerServed(name);<br><br> System.out.println(" –getConnection finishes for thread " + name<br> + ".");<br> return new HackedConnection(conn);<br> }<br>…delegate methods…<br>
To summarize: If no free connections in connection-pool are available, and a new block of ID's is requested, an SQL-connection is requested -> thread has to wait for available SQLConnections. Since this fetching is done in a syncronized block, all other theads that use the ID-generator are blocked. Since they are blocked, they can't return the SQL-connection they use to the pool -> locked untill the pools maxWaitTime expires and the waiting thread gets an exception on the getConnection().