1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.caleigo.core.service;
20
21
22 import java.util.*;
23 import java.sql.*;
24
25 import org.caleigo.core.*;
26 import org.caleigo.core.exception.*;
27 import org.caleigo.toolkit.log.*;
28
29 /*** <Description of JDBCDataService>
30 *
31 * @author Dennis Zikovic
32 * @version 1.00
33 *
34 *//*
35 *
36 * WHEN WHO WHY & WHAT
37 * ------------------------------------------------------------------------------
38 * 2001-07-20 Dennis Zikovic Creation
39 */
40 public class JDBCPreparedStatementDataService extends AbstractDataService
41 {
42
43 public static final int DEFAULT_MAX_CONNECTION_POOL_SIZE = 10;
44
45 private static final int UNDEFINED_AUTO_INDEX = 0;
46 private static final int JDBC_AUTO_INDEX = 1;
47 private static final int SQLSERVER_AUTO_INDEX = 2;
48 private static final int POSTGRE_AUTO_INDEX = 3;
49 private static final int ORACLE_AUTO_INDEX = 4;
50 private static final int HSQLDB_AUTO_INDEX = 5;
51 private static final int UNSUPORTED_AUTO_INDEX = -1;
52
53
54 private static Driver sDriver;
55
56
57 private String mConnectionURL;
58 private Properties mConnectionInfo;
59 private SQLToolKitPS mSQLToolKit;
60 private ConnectionPool mConnectionPool;
61
62 private int mAutoIndexMethod = UNDEFINED_AUTO_INDEX;
63
64
65
66 public static void setDriver(Driver driver)
67 {
68 sDriver = driver;
69 }
70
71
72
73 /*** Default constructor for JDBCDataService.
74 */
75 public JDBCPreparedStatementDataService(IDataSourceDescriptor descriptor)
76 {
77 this(descriptor, descriptor.getSourceName(), "jdbc:odbc:"+descriptor.getSourceName(), new Properties(), DEFAULT_MAX_CONNECTION_POOL_SIZE);
78 }
79
80 public JDBCPreparedStatementDataService(IDataSourceDescriptor descriptor, Object serviceIdentity, String url)
81 {
82 this(descriptor, serviceIdentity, url, new Properties(), DEFAULT_MAX_CONNECTION_POOL_SIZE);
83 }
84
85 public JDBCPreparedStatementDataService(IDataSourceDescriptor descriptor, Object serviceIdentity, String url, String user, String password)
86 {
87 this(descriptor, serviceIdentity, url, new Properties(), DEFAULT_MAX_CONNECTION_POOL_SIZE);
88 mConnectionInfo.put("user", user);
89 mConnectionInfo.put("password", password);
90 }
91
92 public JDBCPreparedStatementDataService(IDataSourceDescriptor descriptor, Object serviceIdentity, String url, String user, String password, String catalog)
93 {
94 this(descriptor, serviceIdentity, url, user, password);
95 if (catalog != null)
96 mConnectionInfo.put("catalog", catalog);
97 }
98
99 public JDBCPreparedStatementDataService(IDataSourceDescriptor descriptor, Object serviceIdentity, String url, java.util.Properties info, int maxConnectionPoolSize)
100 {
101 super(descriptor.getCodeName(), serviceIdentity, descriptor);
102 mConnectionURL = url;
103 mConnectionInfo = info;
104 mSQLToolKit = new SQLToolKitPS();
105 mConnectionPool = new ConnectionPool(maxConnectionPoolSize);
106
107
108 if(DataAccessManager.getManager().getAccessLevel(descriptor)==DataAccessManager.NONE)
109 throw new SecurityException("No read access for "+descriptor+" data sources!");
110 }
111
112
113 public IDataTransaction newTransaction()
114 {
115 return new JDBCDataTransaction();
116 }
117
118 /*** Should return true if the service is online and reponding to calls.
119 */
120 public boolean ping()
121 {
122 boolean responding = false;
123 try
124 {
125 Connection connection = this.openConnection();
126 if(connection!=null)
127 {
128 this.closeConnection(connection);
129 responding = true;
130 }
131 }
132 catch(Exception e)
133 {
134 }
135 return responding;
136 }
137
138
139
140 protected void executeLoad(Connection connection, IEntity entity, Qualifier qualifier) throws DataServiceException
141 {
142 try
143 {
144
145 if(DataAccessManager.getManager().getAccessLevel(entity.getEntityDescriptor())==DataAccessManager.NONE)
146 throw new SecurityException("No read access for "+entity.getEntityDescriptor()+" entities!");
147
148
149 PreparedStatement preparedStatement = mSQLToolKit.buildSelectStatement(entity.getEntityDescriptor(), qualifier, connection);
150
151 ResultSet set = preparedStatement.executeQuery();
152
153
154 if(set.next())
155 {
156 this.readResultSetRow(set, entity, false);
157
158
159 if(!DataAccessManager.getManager().hasReadAccess(entity))
160 entity.clear();
161 else
162 {
163 entity.setStatusFlag(IEntity.PERSISTENT);
164 entity.clearStatusFlag(IEntity.DIRTY | IEntity.EMPTY);
165 }
166 }
167 else
168 {
169 entity.setStatusFlag(IEntity.EMPTY);
170 entity.clearStatusFlag(IEntity.DIRTY | IEntity.PERSISTENT);
171 }
172 set.close();
173 }
174 catch(SQLException e)
175 {
176 throw new DataServiceException("Select command failed", e);
177 }
178 }
179
180 protected void executeQuery(Connection connection, DataQuery query, ISelection selection) throws DataServiceException
181 {
182 try
183 {
184
185 if(DataAccessManager.getManager().getAccessLevel(query.getEntityDescriptor())==DataAccessManager.NONE)
186 throw new SecurityException("No read access for "+query.getEntityDescriptor()+" entities!");
187
188
189 PreparedStatement statement = mSQLToolKit.buildSelectStatement(query.getEntityDescriptor(), query.getQualifier(), connection);
190
191 ResultSet set = statement.executeQuery();
192
193
194 IEntity entity;
195 while(set.next())
196 {
197
198 entity = query.getEntityDescriptor().createEntity();
199 this.readResultSetRow(set, entity, true);
200
201
202 entity.setStatusFlag(IEntity.PERSISTENT);
203 entity.clearStatusFlag(IEntity.DIRTY | IEntity.EMPTY);
204
205
206 if(DataAccessManager.getManager().hasReadAccess(entity))
207 selection.addEntity(entity);
208 }
209 set.close();
210 }
211 catch(SQLException e)
212 {
213 throw new DataServiceException("Select command failed", e);
214 }
215 }
216
217 protected void executeInsert(Connection connection, IEntity entity) throws DataServiceException
218 {
219 String sql = null;
220 try
221 {
222
223 this.checkEntityAsStorable(entity);
224
225
226
227
228 PreparedStatement statement = mSQLToolKit.buildInsertStatement(entity, connection, true);
229
230 Log.print(this, "Performing Insert: "+sql);
231
232
233 int count = -1;
234 if ((mAutoIndexMethod==JDBC_AUTO_INDEX || mAutoIndexMethod==UNDEFINED_AUTO_INDEX) && (mConnectionURL.indexOf("hsqldb")==-1))
235 {
236 try
237 {
238 count = statement.executeUpdate();
239 }
240 catch(Exception e)
241 {
242 }
243 }
244 if(count<0)
245 {
246 statement = mSQLToolKit.buildInsertStatement(entity, connection, false);
247 count = statement.executeUpdate();
248 }
249
250
251 if(count!=1)
252 throw new DataServiceException("Multiple insert commands are dissabled, affect count "+count+" caused rejection.");
253
254
255 if(this.hasAutoIndexField(entity.getEntityDescriptor()))
256 this.updateAutoIndex(statement, entity);
257 }
258 catch(SQLException e)
259 {
260 throw new DataServiceException("Insert command failed", e, sql);
261 }
262 }
263
264 protected void executeUpdate(Connection connection, IEntity entity, Qualifier qualifier) throws DataServiceException
265 {
266 try
267 {
268
269 this.checkEntityAsStorable(entity);
270
271
272
273
274 if(!entity.isDirty())
275 {
276 Log.printWarning(this, "Ignored update of non-dirty entity: "+entity);
277 return;
278 }
279
280
281 PreparedStatement statement = mSQLToolKit.buildUpdateStatement(entity, qualifier, connection);
282
283 int count = statement.executeUpdate();
284
285
286 if(count!=1)
287 throw new DataServiceException("Multiple update commands are dissabled, affect count "+count+" caused rejection.");
288 }
289 catch(SQLException e)
290 {
291 throw new DataServiceException("Update command failed", e);
292 }
293 }
294
295 protected void executeDelete(Connection connection, IEntity entity) throws DataServiceException
296 {
297 try
298 {
299
300 this.checkEntityAsDeletable(entity);
301
302
303
304
305 PreparedStatement statement = mSQLToolKit.buildDeleteStatement(entity.getEntityDescriptor(), entity.getOriginQualifier(), connection);
306
307 int count = statement.executeUpdate();
308 if(count>1)
309 throw new DataServiceException("Multiple delete commands are dissabled, affect count "+count+" caused rejection.");
310 }
311 catch(SQLException e)
312 {
313 throw new DataServiceException("Delete command failed", e);
314 }
315 }
316
317
318
319 protected Connection openConnection() throws DataServiceException
320 {
321 return mConnectionPool.getConnection();
322 }
323
324 protected void closeConnection(Connection connection) throws DataServiceException
325 {
326 mConnectionPool.releaseConnection(connection);
327 }
328
329 protected void readResultSetRow(ResultSet set, IEntity entity, boolean useFastSetData) throws DataServiceException
330 {
331 try
332 {
333 IEntityDescriptor descriptor = entity.getEntityDescriptor();
334 SQLToolKitPS.IDataTypeConverter converter =null;
335 DataType dataType = null;
336 Object data = null;
337
338 for(int j=0; j<set.getMetaData().getColumnCount(); j++)
339 {
340
341 dataType = descriptor.getFieldDescriptor(j).getDataType();
342
343
344 converter = mSQLToolKit.getDataTypeConverter(dataType);
345 data = this.getSetData(set, j+1, dataType);
346 if(converter!=null)
347 data = converter.convertFromDB(data);
348 else if(data!=null && data.getClass()!=dataType.getDataClass())
349 data = dataType.convertFrom(data);
350
351
352
353 if(useFastSetData)
354 this.setEntityData(entity, j, data);
355 else
356 entity.setData(descriptor.getFieldDescriptor(j), data);
357 }
358 }
359 catch(Exception e)
360 {
361 throw new DataServiceException("Select command failed", e);
362 }
363 }
364
365 protected Object getSetData(ResultSet set, int index, DataType dataType)
366 {
367 try
368 {
369 Object data = set.getObject(index);
370
371
372 if(data==null && !set.wasNull())
373 {
374 if(dataType==DataType.STRING)
375 data = set.getString(index);
376 else if(dataType==DataType.BYTE)
377 data = new Byte(set.getByte(index));
378 else if(dataType==DataType.SHORT)
379 data = new Short(set.getShort(index));
380 else if(dataType==DataType.INTEGER)
381 data = new Integer(set.getInt(index));
382 else if(dataType==DataType.LONG)
383 data = new Long(set.getLong(index));
384 else if(dataType==DataType.FLOAT)
385 data = new Float(set.getFloat(index));
386 else if(dataType==DataType.DOUBLE)
387 data = new Double(set.getDouble(index));
388 else if(dataType==DataType.BOOLEAN)
389 data = new Boolean(set.getBoolean(index));
390 else if(dataType instanceof DataType.BinaryType)
391 data = dataType.convertFrom(set.getBinaryStream(index));
392 }
393 return data;
394 }
395 catch(Exception e)
396 {
397 throw new DataServiceException("Select command failed", e);
398 }
399 }
400
401 /*** This method updates autogenerated primary key field values.
402 * Note that this method can only be used directly after a insert and
403 * should still not be considered safe in an environment with frequent
404 * inserts to the entity's table.
405 */
406 protected void updateAutoIndex(Statement statement, IEntity entity) throws DataServiceException
407 {
408 String com = null;
409 IFieldDescriptor field = null;
410
411 if(mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
412 Log.print(this, "Evaluating usable auto index method.");
413
414
415 if((mAutoIndexMethod==JDBC_AUTO_INDEX || mAutoIndexMethod==UNDEFINED_AUTO_INDEX) && (mConnectionURL.indexOf("hsqldb")==-1))
416 {
417 try
418 {
419 ResultSet set = statement.getGeneratedKeys();
420 for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
421 {
422 field = entity.getEntityDescriptor().getFieldDescriptor(j);
423 if(field.isAutoGenerated() && field.isIdentityField())
424 {
425
426 if(set.next())
427 {
428 Object data = field.getDataType().convertFrom(set.getObject(1));
429 entity.setData(field, data);
430
431 mAutoIndexMethod = JDBC_AUTO_INDEX;
432 }
433 }
434 }
435 while(set.next());
436 }
437 catch(Exception e)
438 {
439 if(mAutoIndexMethod==JDBC_AUTO_INDEX)
440 throw new DataServiceException("Update of auto generated index failed!", e, com);
441 }
442 if(mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
443 Log.print(this, "JDBC method not applicable.");
444 }
445
446
447 if(mAutoIndexMethod==POSTGRE_AUTO_INDEX || mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
448 {
449 try
450 {
451 for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
452 {
453 field = entity.getEntityDescriptor().getFieldDescriptor(j);
454 if(field.isAutoGenerated() && field.isIdentityField())
455 {
456 com = "SELECT currval('"+field.getEntityDescriptor().getSourceName()+"_"+field.getSourceName()+"_seq') ";
457 Statement identityStatement = statement.getConnection().createStatement();
458 Log.print(this, "Key retrieval: "+com);
459 ResultSet set = identityStatement.executeQuery(com);
460
461
462 if(set.next())
463 {
464 Object data = field.getDataType().convertFrom(set.getObject(1));
465 entity.setData(field, data);
466
467 mAutoIndexMethod = POSTGRE_AUTO_INDEX;
468 }
469 while(set.next());
470 }
471 }
472 }
473 catch(Exception e)
474 {
475 Log.printWarning(this, "Postgre method failed!");
476 if(mAutoIndexMethod==POSTGRE_AUTO_INDEX)
477 throw new DataServiceException("Update of auto generated index failed!", e, com);
478 }
479 if(mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
480 Log.print(this, "Postgre method not applicable.");
481 }
482
483
484 if(mAutoIndexMethod==SQLSERVER_AUTO_INDEX || mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
485 {
486 try
487 {
488 for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
489 {
490 field = entity.getEntityDescriptor().getFieldDescriptor(j);
491 if(field.isAutoGenerated() && field.isIdentityField())
492 {
493 com = "SELECT @@IDENTITY FROM " + field.getEntityDescriptor().getSourceName();
494 Statement identityStatement = statement.getConnection().createStatement();
495 Log.print(this, "Key retrieval: "+com);
496 ResultSet set = identityStatement.executeQuery(com);
497
498
499 if(set.next())
500 {
501 Object data = field.getDataType().convertFrom(set.getObject(1));
502 entity.setData(field, data);
503
504 mAutoIndexMethod = SQLSERVER_AUTO_INDEX;
505 }
506 while(set.next());
507 }
508 }
509 }
510 catch(Exception e)
511 {
512 if(mAutoIndexMethod==SQLSERVER_AUTO_INDEX)
513 throw new DataServiceException("Update of auto generated index failed!", e, com);
514 }
515 if(mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
516 Log.print(this, "SQL-Server method not applicable.");
517 }
518
519
520 if(mAutoIndexMethod==ORACLE_AUTO_INDEX || mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
521 {
522 try
523 {
524 for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
525 {
526 field = entity.getEntityDescriptor().getFieldDescriptor(j);
527 if(field.isAutoGenerated() && field.isIdentityField())
528 {
529 com = "SELECT " + field.getEntityDescriptor().getSourceName() + "_" + field.getSourceName() + "_seq.currval FROM dual " + field.getEntityDescriptor().getSourceName();
530 Statement identityStatement = statement.getConnection().createStatement();
531 Log.print(this, "Key retrieval: "+com);
532 ResultSet set = identityStatement.executeQuery(com);
533
534
535 if(set.next())
536 {
537 Object data = field.getDataType().convertFrom(set.getObject(1));
538 entity.setData(field, data);
539
540 mAutoIndexMethod = ORACLE_AUTO_INDEX;
541 }
542 while(set.next());
543 }
544 }
545 }
546 catch(Exception e)
547 {
548 if(mAutoIndexMethod==ORACLE_AUTO_INDEX)
549 throw new DataServiceException("Update of auto generated index failed!", e, com);
550 }
551 if(mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
552 Log.print(this, "Oracle method not applicable.");
553 }
554
555
556 if(mAutoIndexMethod==HSQLDB_AUTO_INDEX || mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
557 {
558 try
559 {
560 for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
561 {
562 field = entity.getEntityDescriptor().getFieldDescriptor(j);
563 if(field.isAutoGenerated() && field.isIdentityField())
564 {
565 com = "call identity()";
566 Statement identityStatement = statement.getConnection().createStatement();
567 Log.print(this, "Key retrieval: "+com);
568 ResultSet set = identityStatement.executeQuery(com);
569
570
571 if(set.next())
572 {
573 Object data = field.getDataType().convertFrom(set.getObject(1));
574 entity.setData(field, data);
575
576 mAutoIndexMethod = HSQLDB_AUTO_INDEX;
577 }
578 while(set.next());
579 }
580 }
581 }
582 catch(Exception e)
583 {
584 if(mAutoIndexMethod==HSQLDB_AUTO_INDEX)
585 throw new DataServiceException("Update of auto generated index failed!", e, com);
586 }
587 if(mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
588 Log.print(this, "hsqldb method not applicable.");
589 }
590
591
592
593 if(mAutoIndexMethod==UNSUPORTED_AUTO_INDEX || mAutoIndexMethod==UNDEFINED_AUTO_INDEX)
594 {
595 mAutoIndexMethod=UNSUPORTED_AUTO_INDEX;
596 throw new DataServiceException("Used JDBC Driver does not support extraction of Generated Keys!");
597 }
598 }
599
600 protected boolean hasAutoIndexField(IEntityDescriptor entityDescriptor)
601 {
602 IFieldDescriptor autoIndexField = null;
603 for(int j=0; autoIndexField==null && j<entityDescriptor.getFieldCount(); j++)
604 if(entityDescriptor.getFieldDescriptor(j).isAutoGenerated() && entityDescriptor.getFieldDescriptor(j).isIdentityField())
605 autoIndexField = entityDescriptor.getFieldDescriptor(j);
606 return autoIndexField!=null;
607 }
608
609
610 public String getURL()
611 {
612 return mConnectionURL;
613 }
614
615 public String getUser()
616 {
617 return mConnectionInfo.getProperty("user", null);
618 }
619
620 public String getPassword()
621 {
622 return mConnectionInfo.getProperty("password", null);
623 }
624
625 public SQLToolKitPS getSQLToolKit()
626 {
627 return mSQLToolKit;
628 }
629
630 public void setSQLToolKit(SQLToolKitPS kit)
631 {
632 mSQLToolKit = kit;
633 }
634
635
636 protected class JDBCDataTransaction extends AbstractDataTransaction
637 {
638
639 private Connection mConnection;
640
641
642 public JDBCDataTransaction()
643 {
644 super(getTimeout());
645 }
646
647
648
649 /*** Commit performs all the stored operations in the transaction.
650 * If any of the operations fail a rollback on all operations will be
651 * automatically performed and a TransactionFailedException will be thrown.
652 */
653 public void commit() throws DataServiceException
654 {
655 long commitStartTime = System.currentTimeMillis();
656
657
658 mConnection = openConnection();
659
660
661 if(System.currentTimeMillis()-commitStartTime>50)
662 Log.printWarning(this, "Long connection open time: " + (System.currentTimeMillis() - commitStartTime) + " ms");
663
664
665 try
666 {
667 try
668 {
669 if(mConnectionInfo.containsKey("catalog"))
670 mConnection.setCatalog((String) mConnectionInfo.get("catalog"));
671 }
672 catch (SQLException e)
673 {
674 }
675
676
677 mConnection.setAutoCommit(false);
678 Enumeration dataOperations = this.getOperations();
679 while(dataOperations.hasMoreElements())
680 {
681 DataOperation operation = (DataOperation)dataOperations.nextElement();
682 switch(operation.getOperationType())
683 {
684 case DataOperation.LOAD:
685 executeLoad(mConnection, operation.getEntity(), operation.getQualifier());
686 break;
687 case DataOperation.QUERY:
688 executeQuery(mConnection, operation.getDataQuery(), operation.getEntitySelection());
689 break;
690 case DataOperation.STORE:
691 if(operation.getEntity().isPersistent())
692 executeUpdate(mConnection, operation.getEntity(), operation.getQualifier());
693 else
694 executeInsert(mConnection, operation.getEntity());
695 break;
696 case DataOperation.DELETE:
697 executeDelete(mConnection, operation.getEntity());
698 break;
699 case DataOperation.REFRESH:
700 executeLoad(mConnection, operation.getEntity(), operation.getQualifier());
701 break;
702 }
703 }
704
705
706 mConnection.commit();
707 Log.print(this, "Commit time: " + (System.currentTimeMillis() - commitStartTime) + " ms");
708
709
710 dataOperations = this.getOperations();
711 while(dataOperations.hasMoreElements())
712 {
713 DataOperation operation = (DataOperation)dataOperations.nextElement();
714 switch(operation.getOperationType())
715 {
716 case DataOperation.DELETE:
717 operation.getEntity().clearStatusFlag(IEntity.DIRTY | IEntity.EMPTY | IEntity.PERSISTENT);
718 break;
719 case DataOperation.STORE:
720 operation.getEntity().setStatusFlag(IEntity.PERSISTENT);
721 operation.getEntity().clearStatusFlag(IEntity.DIRTY | IEntity.EMPTY);
722 break;
723 case DataOperation.REFRESH:
724 case DataOperation.LOAD:
725 case DataOperation.QUERY:
726 break;
727 }
728 }
729 }
730 catch(Exception e)
731 {
732 String rollbackMsg;
733
734
735 try
736 {
737 mConnection.rollback();
738 rollbackMsg = "rollback succesfull";
739 }
740 catch(SQLException eSQL)
741 {
742 rollbackMsg = "rollback failed";
743 }
744
745
746 if(e instanceof DataServiceException)
747 throw new DataServiceException("Transaction failed, "+rollbackMsg, e, ((DataServiceException)e).getDescription());
748 else
749 throw new DataServiceException("Transaction failed, "+rollbackMsg+": "+e.getClass().getName()+" - "+e.getMessage(), e);
750 }
751 finally
752 {
753
754 closeConnection(mConnection);
755 mConnection = null;
756 }
757
758
759 }
760
761 public void abortTransaction() throws DataServiceException
762 {
763 try
764 {
765 if (mConnection != null)
766 {
767 if (mConnection.getAutoCommit() == false)
768 mConnection.rollback();
769
770 mConnection.close();
771 mConnection = null;
772 }
773 } catch (SQLException e)
774 {
775 throw new DataServiceException("Failed to abort transaction", e);
776 }
777 }
778 }
779
780 protected class ConnectionPool
781 {
782
783 private int mMaxSize;
784 private Connection[] mConnectionPool;
785 private boolean[] mFreeConnectionFlags;
786 private List mWaitingThreads;
787
788
789
790 /*** Creates a connection pool with the maximum size <code>maxSize</code>.
791 * Setting the maximum size to zero means that every call to <code>getConnection</code>
792 * will create a new connection.
793 */
794 public ConnectionPool(int maxSize)
795 {
796 mMaxSize = maxSize;
797 if (maxSize > 0)
798 {
799 mConnectionPool = new Connection[maxSize];
800 mFreeConnectionFlags = new boolean[maxSize];
801 mWaitingThreads = new ArrayList();
802 }
803 }
804
805
806 public Connection getConnection() throws DataServiceException
807 {
808
809 if(mMaxSize == 0)
810 {
811 try
812 {
813 if (sDriver != null)
814 return sDriver.connect(mConnectionURL, mConnectionInfo);
815
816 if (mConnectionInfo.get("user") != null)
817 return DriverManager.getConnection(mConnectionURL, mConnectionInfo);
818 else
819 return DriverManager.getConnection(mConnectionURL);
820 }
821 catch(SQLException e)
822 {
823 throw new DataServiceException("Failed to open database connection: " + e.getMessage());
824 }
825 }
826
827
828
829 try
830 {
831 return this.getConnectionFromPool();
832
833
834 }
835 catch (Exception e)
836 {
837 throw new DataServiceException("Failed to open database connection: " + e.getMessage());
838 }
839
840 }
841
842 public int getMaxSize()
843 {
844 return mMaxSize;
845 }
846
847
848 public synchronized void releaseConnection(Connection connection)
849 throws DataServiceException
850 {
851 if(connection == null)
852 return;
853
854
855 if(mMaxSize == 0)
856 {
857 try
858 {
859 connection.close();
860 return;
861 }
862 catch(SQLException e)
863 {
864 throw new DataServiceException("Failed to open database connection: " + e.getMessage());
865 }
866 }
867
868
869 int index = 0;
870 for(; index < mConnectionPool.length && mConnectionPool[index] != connection; index++);
871
872
873 if(index < mConnectionPool.length)
874 {
875 mFreeConnectionFlags[index] = true;
876 this.notifyAll();
877 }
878 }
879
880
881 private synchronized Connection getConnectionFromPool()
882 throws SQLException, InterruptedException
883 {
884 Connection conn = null;
885
886 if(mWaitingThreads.size() == 0)
887 conn = this.getNextFreeConnectionFromPool();
888
889
890 if(conn == null)
891 mWaitingThreads.add(Thread.currentThread());
892
893 while(conn == null)
894 {
895 Log.print(this, "Waiting for free connection.");
896 this.wait();
897
898 if (mWaitingThreads.get(0) == Thread.currentThread())
899 conn = this.getNextFreeConnectionFromPool();
900 }
901
902
903 if(mWaitingThreads.contains(Thread.currentThread()));
904 mWaitingThreads.remove(Thread.currentThread());
905
906 return conn;
907 }
908
909 private synchronized Connection getNextFreeConnectionFromPool()
910 throws SQLException
911 {
912 int index = 0;
913
914
915 for(; index < mConnectionPool.length && mConnectionPool[index] != null && !mFreeConnectionFlags[index]; index++);
916
917 if (index >= mConnectionPool.length)
918
919
920 return null;
921
922 if(mConnectionPool[index] != null)
923 {
924
925 mFreeConnectionFlags[index] = false;
926
927
928 if (mConnectionPool[index].isClosed())
929 mConnectionPool[index] = this.openNewConnection();
930
931 return mConnectionPool[index];
932 }
933
934
935 mConnectionPool[index] = this.openNewConnection();
936
937 Log.print(this, "Extending connection pool for "+getServiceIdentity()+" to " + (index+1) + " connections.");
938 return mConnectionPool[index];
939 }
940
941 private Connection openNewConnection() throws SQLException
942 {
943 if (sDriver != null)
944 return sDriver.connect(mConnectionURL, mConnectionInfo);
945
946 if (mConnectionInfo.get("user") != null)
947 return DriverManager.getConnection(mConnectionURL, mConnectionInfo);
948 else
949 return DriverManager.getConnection(mConnectionURL);
950 }
951 }
952 }