1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.melati.poem;
44
45 import org.melati.poem.dbms.Dbms;
46 import org.melati.poem.dbms.DbmsFactory;
47 import org.melati.poem.transaction.Transaction;
48 import org.melati.poem.transaction.TransactionPool;
49 import org.melati.poem.util.*;
50
51 import java.sql.*;
52 import java.util.*;
53 import java.util.concurrent.locks.ReadWriteLock;
54 import java.util.concurrent.locks.ReentrantReadWriteLock;
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public abstract class Database implements TransactionPool {
70
71 final Database _this = this;
72 private final ReadWriteLock lock = new ReentrantReadWriteLock();
73 private final boolean[] connecting = new boolean[1];
74 private Vector<Transaction> transactions = null;
75 private Vector<Transaction> freeTransactions = null;
76 private Connection committedConnection;
77 private long structureSerial = 0L;
78 private Vector<Table<?>> tables = new Vector<Table<?>>();
79 private Hashtable<String, Table<?>> tablesByName = new Hashtable<String, Table<?>>();
80 private Table<?>[] displayTables = null;
81 private String name;
82 private String displayName;
83 private Dbms dbms;
84 private boolean logSQL = false;
85 private boolean logCommits = false;
86 private int transactionsMax;
87 private String connectionUrl;
88
89
90
91 private int queryCount = 0;
92
93
94
95
96
97
98
99
100
101
102 private String lastQuery = null;
103 private boolean initialised = false;
104 private User guest = null;
105 private User administrator = null;
106 private UserCapabilityCache capabilityCache = new UserCapabilityCache();
107 private Capability canAdminister = null;
108
109
110
111
112
113
114 public Database() {
115 }
116
117
118
119
120 private synchronized void init() {
121 if (!initialised) {
122 for (Table<?> t : this.tables)
123 t.init();
124 initialised = true;
125 }
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186 @SuppressWarnings("unchecked")
187 public void connect(String nameIn, String dbmsclass, String url,
188 String username, String password,
189 int transactionsMaxP) throws PoemException {
190
191 this.name = nameIn;
192 this.connectionUrl = url;
193
194 synchronized (connecting) {
195 if (connecting[0])
196 throw new ConnectingException();
197 connecting[0] = true;
198 }
199
200 if (committedConnection != null)
201 throw new ReconnectionPoemException(this);
202
203 setDbms(DbmsFactory.getDbms(dbmsclass));
204
205 setTransactionsMax(transactionsMaxP);
206 committedConnection = getDbms().getConnection(url, username, password);
207 transactions = new Vector<Transaction>();
208 for (int s = 0; s < transactionsMax(); ++s)
209 transactions.add(
210 new PoemTransaction(
211 this,
212 getDbms().getConnection(url, username, password),
213 s));
214
215 freeTransactions = (Vector<Transaction>)transactions.clone();
216
217 try {
218
219 init();
220
221
222 DatabaseMetaData m = committedConnection.getMetaData();
223 getTableInfoTable().unifyWithDB(
224 m.getColumns(null, getSchema(),
225 unreservedName(getTableInfoTable().getName()), null), unreservedName("id"));
226 getColumnInfoTable().unifyWithDB(
227 m.getColumns(null, getSchema(),
228 unreservedName(getColumnInfoTable().getName()), null), unreservedName("id"));
229 getTableCategoryTable().unifyWithDB(
230 m.getColumns(null, getSchema(),
231 unreservedName(getTableCategoryTable().getName()), null), unreservedName("id"));
232
233 inSession(AccessToken.root,
234 new PoemTask() {
235 public void run() throws PoemException {
236 try {
237 _this.unifyWithDB();
238 }
239 catch (SQLException e) {
240 throw new SQLPoemException(e);
241 }
242 }
243
244 public String toString() {
245 return "Unifying with DB";
246 }
247 });
248 } catch (Exception e) {
249 if (committedConnection != null) disconnect();
250 throw new UnificationPoemException(e);
251 } finally {
252 synchronized (connecting) {
253 connecting[0] = false;
254 }
255 }
256 }
257
258
259
260
261 public void disconnect() throws PoemException {
262 if (committedConnection == null)
263 throw new ReconnectionPoemException(this);
264
265 try {
266 for (Transaction poemTransaction : freeTransactions){
267 ((PoemTransaction)poemTransaction).getConnection().close();
268 }
269 freeTransactions.removeAllElements();
270
271 getDbms().shutdown(committedConnection);
272 committedConnection.close();
273 } catch (SQLException e) {
274 throw new SQLPoemException(e);
275 }
276 committedConnection = null;
277 }
278
279
280
281
282
283
284
285
286
287 protected synchronized void defineTable(Table<?> table)
288 throws DuplicateTableNamePoemException {
289 if (getTableIgnoringCase(table.getName()) != null)
290 throw new DuplicateTableNamePoemException(this, table.getName());
291 redefineTable(table);
292 }
293
294 protected synchronized void redefineTable(Table<?> table) {
295 if (table.getDatabase() != this)
296 throw new TableInUsePoemException(this, table);
297
298 if (getTableIgnoringCase(table.getName()) == null) {
299 tablesByName.put(table.getName().toLowerCase(), table);
300 tables.addElement(table);
301 }
302 else
303 tables.setElementAt(table,
304 tables.indexOf(
305 tablesByName.put(table.getName().toLowerCase(), table)));
306 displayTables = null;
307 }
308
309 private ResultSet columnsMetadata(DatabaseMetaData m, String tableName)
310 throws SQLException {
311 return m.getColumns(null, getSchema(), unreservedName(tableName), null);
312 }
313
314 protected String getSchema() {
315 return null;
316 }
317
318 protected String unreservedName(String name) {
319 return dbms.unreservedName(name);
320 }
321
322 protected String getJdbcMetadataName(String unreservedName) {
323 return dbms.getJdbcMetadataName(unreservedName);
324 }
325
326 protected String melatiName(String name) {
327 return dbms.melatiName(name);
328 }
329
330
331
332
333
334
335 @SuppressWarnings({ "unchecked", "rawtypes" })
336 public Table<?> addTableAndCommit(TableInfo info, String troidName)
337 throws PoemException {
338
339
340
341
342 Table<?> table = new JdbcTable<Persistent>(this, info.getName(),
343 DefinitionSource.infoTables);
344 table.defineColumn(new ExtraColumn(table, troidName,
345 TroidPoemType.it,
346 DefinitionSource.infoTables,
347 table.getNextExtrasIndex()));
348 table.setTableInfo(info);
349 table.unifyWithColumnInfo();
350 table.unifyWithDB(null,troidName);
351
352 PoemThread.commit();
353 defineTable(table);
354
355 return table;
356 }
357
358
359
360
361 public void deleteTableAndCommit(TableInfo info) {
362 try {
363 Table<?> table = info.actualTable();
364 Enumeration<Column<?>> columns = table.columns();
365 while (columns.hasMoreElements()){
366 Column<?> c = columns.nextElement();
367 table.deleteColumnAndCommit(c.getColumnInfo());
368 }
369
370 info.delete();
371 beginStructuralModification();
372 table.dbModifyStructure(" DROP TABLE " + table.quotedName());
373 synchronized (tables) {
374 tables.remove(table);
375 tablesByName.remove(table.getName().toLowerCase());
376 if (displayTables != null)
377 displayTables = (Table[])ArrayUtils.removed(displayTables, table);
378 uncache();
379 table.invalidateTransactionStuffs();
380 }
381 PoemThread.commit();
382 }
383 finally {
384 endStructuralModification();
385 }
386 }
387
388 private String getTroidColumnName(DatabaseMetaData m, String tableName) throws SQLException {
389 String troidColumnName = null;
390 System.err.println("Looking for " + unreservedName(tableName));
391 ResultSet tables = m.getTables(null, getSchema(), unreservedName(tableName), null);
392 if (tables.next()) {
393 ResultSet r = m.getPrimaryKeys(null, getSchema(), unreservedName(tableName));
394 while (r.next())
395 troidColumnName = r.getString("COLUMN_NAME");
396 r.close();
397
398 if (troidColumnName != null) {
399 log(getJdbcMetadataName(unreservedName(troidColumnName)));
400 ResultSet idCol = m.getColumns(null, getSchema(), unreservedName(tableName), getJdbcMetadataName(unreservedName(troidColumnName)));
401 log("Discovered a primary key troid candidate column for jdbc table :" + tableName + ":" + troidColumnName);
402 if (idCol.next()) {
403 if (dbms.canRepresent(defaultPoemTypeOfColumnMetaData(idCol), TroidPoemType.it) == null)
404 if (troidColumnName.equals("id"))
405
406 throw new UnificationPoemException("Primary Key " + troidColumnName + " cannot represent a Troid");
407 else {
408 log("Column " + troidColumnName + " cannot represent troid as it has type " + defaultPoemTypeOfColumnMetaData(idCol));
409 ResultSet u;
410 try {
411 u = m.getIndexInfo
412 (null, getSchema(), unreservedName(tableName), true, false);
413 } catch (SQLException e) {
414 throw new RuntimeException("IndexInfo not found for " + unreservedName(tableName), e);
415 }
416 String unusableKey = troidColumnName;
417 troidColumnName = null;
418 String uniqueKey = null;
419 String foundKey = null;
420 while (u.next()) {
421 uniqueKey = u.getString("COLUMN_NAME");
422 if (!uniqueKey.equals(unusableKey)) {
423 ResultSet idColNotPrimeKey = m.getColumns(null,
424 getSchema(),
425 unreservedName(tableName),
426 getJdbcMetadataName(unreservedName(uniqueKey)));
427 if (idColNotPrimeKey.next()) {
428 if (idColNotPrimeKey.getInt("NULLABLE") != DatabaseMetaData.columnNoNulls) {
429 idColNotPrimeKey.close();
430 break;
431 }
432 SQLPoemType<?> t = defaultPoemTypeOfColumnMetaData(idColNotPrimeKey);
433 if (dbms.canRepresent(t, TroidPoemType.it) == null) {
434 log("Unique Column " + uniqueKey + " cannot represent troid as it has type " + t);
435 uniqueKey = null;
436 }
437 if (uniqueKey != null) {
438 if (foundKey != null) {
439 idColNotPrimeKey.close();
440 throw new UnificationPoemException(
441 "Second unique, non-nullable numeric index found :" + uniqueKey
442 + " already found " + foundKey);
443 }
444 log("Unique Column " + uniqueKey + " can represent troid as it has type " + t);
445 foundKey = uniqueKey;
446 }
447 idColNotPrimeKey.close();
448 } else
449 throw new UnexpectedExceptionPoemException(
450 "Found a unique key but no corresponding column");
451
452 troidColumnName = uniqueKey;
453 }
454 }
455 u.close();
456 }
457 } else
458 throw new UnexpectedExceptionPoemException(
459 "Found a primary key but no corresponding column");
460 idCol.close();
461 }
462 tables.close();
463 }
464 return troidColumnName;
465 }
466
467
468
469
470
471
472
473 private synchronized void unifyWithDB() throws PoemException, SQLException {
474 boolean debug = true;
475
476
477
478
479 for (Enumeration<TableInfo> ti = getTableInfoTable().selection();
480 ti.hasMoreElements();) {
481 TableInfo tableInfo = ti.nextElement();
482 Table<?> table = getTableIgnoringCase(tableInfo.getName());
483 if (table == null) {
484 if (debug) log("Defining table:" + tableInfo.getName());
485 table = new JdbcTable<Persistent>(this, tableInfo.getName(),
486 DefinitionSource.infoTables);
487 defineTable(table);
488 }
489 table.setTableInfo(tableInfo);
490 }
491
492
493
494 for (Table<?> t : tables)
495 t.createTableInfo();
496
497
498
499 for (Table<?> t : tables)
500 t.unifyWithColumnInfo();
501
502
503
504 DatabaseMetaData m = committedConnection.getMetaData();
505 ResultSet tableTypes = m.getTableTypes();
506 while (tableTypes.next()) {
507 System.err.println("Type: " + tableTypes.getString(1));
508 }
509 List<HashMap<String, String>> tableDescs = getRelevantTables(m);
510 int tableCount = 0;
511 for (HashMap<String, String> tableDesc : tableDescs) {
512 tableCount++;
513 if (debug) log("Table:" + tableDesc.get("TABLE_NAME") +
514 " Type:" + tableDesc.get("TABLE_TYPE"));
515 String tableName = melatiName(tableDesc.get("TABLE_NAME"));
516 if (debug) log("Melati Table name :" + tableName);
517 Table<?> table = null;
518 String troidColumnName = null;
519 if (tableName != null) {
520 table = getTableIgnoringCase(tableName);
521 if (table == null) {
522 if (debug) log("Unknown to POEM, with JDBC name " + tableName);
523
524
525 troidColumnName = getTroidColumnName(m, unreservedName(tableName));
526 if(debug) log("Primary key:"+ troidColumnName);
527 if (troidColumnName != null) {
528 if (debug) log("Got a troid column for discovered jdbc table :" + tableName + ":" + troidColumnName);
529 try {
530 table = new JdbcTable<Persistent>(this, tableName,
531 DefinitionSource.sqlMetaData);
532 defineTable(table);
533 }
534 catch (DuplicateTableNamePoemException e) {
535 throw new UnexpectedExceptionPoemException(e);
536 }
537 table.createTableInfo();
538 } else log ("Ignoring table " + tableName + " as it has no plausible troid");
539 } else if (debug) log("Table not null:" + tableName + " has name " + table.getName());
540 }
541
542 if (table != null) {
543 if (debug) log("table not null now:" + tableName);
544 if (debug) log("columnsMetadata(m, tableName):"
545 + columnsMetadata(m, tableName));
546
547
548 table.unifyWithDB(columnsMetadata(m, tableName), troidColumnName);
549 } else if (debug) log("table still null, probably doesn't have a troid:" + tableName);
550
551 }
552 System.err.println("Table count:" + tableCount);
553
554
555
556 for (Table<?> table : tables) {
557 if (debug) log("Unifying:" + table.getName() + "(" + unreservedName(table.getName()) + ")");
558
559
560 ResultSet colDescs = columnsMetadata(m,
561 unreservedName(table.getName()));
562 if (!colDescs.next()) {
563
564
565 table.unifyWithDB(null, getTroidColumnName(m, unreservedName(table.getName())));
566 }
567 }
568
569 for (Table<?> table : tables)
570 table.postInitialise();
571
572 }
573
574 protected List<HashMap<String, String>> getRelevantTables(DatabaseMetaData m) throws SQLException {
575 List<HashMap<String, String>> tableMetaData = new ArrayList<HashMap<String, String>>();
576 String[] normalTables = {"TABLE"};
577 ResultSet tables = m.getTables(null, null, null, normalTables);
578 while (tables.next()) {
579 HashMap<String, String> t = new HashMap<String, String>();
580
581 t.put("TABLE_TYPE", tables.getString("TABLE_TYPE"));
582 t.put("TABLE_NAME", tables.getString("TABLE_NAME"));
583
584 tableMetaData.add(t);
585 }
586 return tableMetaData;
587 }
588
589
590
591
592
593
594
595
596
597
598
599 public void addConstraints() {
600 inSession(AccessToken.root,
601 new PoemTask() {
602 public void run() throws PoemException {
603 PoemThread.commit();
604 beginStructuralModification();
605 try {
606 for (Table<?> table : tables)
607 table.dbAddConstraints();
608 PoemThread.commit();
609 }
610 finally {
611 endStructuralModification();
612 }
613 }
614
615 public String toString() {
616 return "Adding constraints to DB";
617 }
618 });
619 }
620
621
622
623
624
625
626
627
628
629
630 public final int transactionsMax() {
631 return transactionsMax;
632 }
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648 public final void setTransactionsMax(int t) {
649 transactionsMax = t;
650 }
651
652
653
654
655
656 public int getTransactionsCount() {
657 return transactions.size();
658 }
659
660
661
662
663
664 public int getFreeTransactionsCount() {
665 return freeTransactions.size();
666 }
667
668
669
670
671
672 private PoemTransaction openTransaction() {
673 synchronized (freeTransactions) {
674 if (freeTransactions.size() == 0)
675 throw new NoMoreTransactionsException("Database " + name + " has no free transactions remaining of "
676 + transactions.size() + " transactions.");
677 PoemTransaction transaction =
678 (PoemTransaction)freeTransactions.lastElement();
679 freeTransactions.setSize(freeTransactions.size() - 1);
680 return transaction; }
681 }
682
683
684
685
686 void notifyClosed(PoemTransaction transaction) {
687 freeTransactions.addElement(transaction);
688 }
689
690
691
692
693
694
695
696
697
698 public PoemTransaction poemTransaction(int index) {
699 return (PoemTransaction)transactions.elementAt(index);
700 }
701
702
703
704
705
706 public final Transaction transaction(int index) {
707 return poemTransaction(index);
708 }
709
710
711
712
713
714
715
716
717
718
719
720 public boolean isFree(PoemTransaction trans) {
721 return freeTransactions.contains(trans);
722 }
723
724
725
726
727 public void beginExclusiveLock() {
728
729 if (PoemThread.inSession()) {
730 lock.readLock().unlock();
731
732 }
733 lock.writeLock().lock();
734 }
735
736
737
738
739 public void endExclusiveLock() {
740 lock.writeLock().unlock();
741
742
743
744 if (PoemThread.inSession())
745 lock.readLock().lock();
746 }
747
748
749
750
751
752
753
754 private void perform(AccessToken accessToken, final PoemTask task,
755 boolean useCommittedTransaction) throws PoemException {
756
757 lock.readLock().lock();
758
759 final PoemTransaction transaction =
760 useCommittedTransaction ? null : openTransaction();
761 try {
762 PoemThread.inSession(new PoemTask() {
763 public void run() throws PoemException {
764 task.run();
765 if (transaction != null)
766 transaction.close(true);
767 }
768
769 public String toString() {
770 return task.toString();
771 }
772 },
773 accessToken,
774 transaction);
775 }
776 finally {
777 try {
778 if (transaction != null && !isFree(transaction)) {
779 transaction.close(false);
780 }
781 } finally {
782
783 lock.readLock().unlock();
784 }
785 }
786 }
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825 public void inSession(AccessToken accessToken, PoemTask task) {
826 perform(accessToken, task, false);
827 }
828
829
830
831
832 public void inSessionAsRoot(PoemTask task) {
833 perform(AccessToken.root, task, false);
834 }
835
836
837
838
839
840
841
842
843
844
845
846
847 public void beginSession(AccessToken accessToken) {
848 lock.readLock().lock();
849 PoemTransaction transaction = openTransaction();
850 try {
851 PoemThread.beginSession(accessToken,transaction);
852 } catch (AlreadyInSessionPoemException e) {
853 notifyClosed(transaction);
854 lock.readLock().unlock();
855 throw e;
856 }
857 }
858
859
860
861
862
863
864
865 public void endSession() {
866 PoemTransaction tx = PoemThread.sessionToken().transaction;
867 PoemThread.endSession();
868 tx.close(true);
869 lock.readLock().unlock();
870 }
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887 public void inCommittedTransaction(AccessToken accessToken, PoemTask task) {
888 perform(accessToken, task, true);
889 }
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905 public final Table<?> getTable(String tableName) throws NoSuchTablePoemException {
906 Table<?> table = getTableIgnoringCase(tableName);
907 if (table == null) throw new NoSuchTablePoemException(this, tableName);
908 return table;
909 }
910
911 private Table<?> getTableIgnoringCase(String tableName) {
912 return tablesByName.get(tableName.toLowerCase());
913 }
914
915
916
917
918
919
920
921
922 public final Enumeration<Table<?>> tables() {
923 return tables.elements();
924 }
925
926
927
928
929
930 public final List<Table<?>> getTables() {
931 return tables;
932 }
933
934
935
936
937
938
939
940 public Enumeration<Table<?>> displayTables() {
941 return displayTables(PoemThread.inSession() ? PoemThread.transaction() : null);
942 }
943
944
945 public List<Table<?>> getDisplayTables() {
946 return EnumUtils.list(displayTables());
947 }
948
949
950
951
952
953
954
955 public Enumeration<Table<?>> displayTables(PoemTransaction transaction) {
956 Table<?>[] displayTablesL = this.displayTables;
957
958 if (displayTablesL == null) {
959 Enumeration<Integer> tableIDs = getTableInfoTable().troidSelection(
960 (String)null ,
961 quotedName("displayorder") + ", " + quotedName("name"),
962 false, transaction);
963
964 Vector<Table<?>> them = new Vector<Table<?>>();
965 while (tableIDs.hasMoreElements()) {
966 Table<?> table =
967 tableWithTableInfoID(tableIDs.nextElement().intValue());
968 if (table != null)
969 them.addElement(table);
970 }
971
972 displayTablesL = new Table[them.size()];
973 them.copyInto(displayTablesL);
974 this.displayTables = displayTablesL;
975 }
976
977 return new ArrayEnumeration<Table<?>>(this.displayTables);
978 }
979
980
981
982
983
984
985
986 Table<?> tableWithTableInfoID(int tableInfoID) {
987 for (Table<?> table : tables) {
988 Integer id = table.tableInfoID();
989 if (id != null && id.intValue() == tableInfoID)
990 return table;
991 }
992 return null;
993 }
994
995
996
997
998 public Enumeration<Column<?>> columns() {
999 return new FlattenedEnumeration<Column<?>>(
1000 new MappedEnumeration<Enumeration<Column<?>>, Table<?>>(tables()) {
1001 public Enumeration<Column<?>> mapped(Table<?> table) {
1002 return table.columns();
1003 }
1004 });
1005 }
1006
1007
1008 public List<Column<?>> getColumns() {
1009 return EnumUtils.list(columns());
1010 }
1011
1012 public int tableCount() {
1013 return tables.size();
1014 }
1015
1016 public int columnCount() {
1017 return getColumns().size();
1018 }
1019
1020 public int recordCount() {
1021 Enumeration<Integer> counts = new MappedEnumeration<Integer, Table<?>>(tables()) {
1022 public Integer mapped(Table<?> table) {
1023 return new Integer(table.count());
1024 }
1025 };
1026 int total = 0;
1027 while(counts.hasMoreElements())
1028 total = total + counts.nextElement().intValue();
1029
1030 return total;
1031 }
1032
1033
1034
1035
1036
1037 Column<?> columnWithColumnInfoID(int columnInfoID) {
1038 for (Table<?> table : tables) {
1039 Column<?> column = table.columnWithColumnInfoID(columnInfoID);
1040 if (column != null)
1041 return column;
1042 }
1043 return null;
1044 }
1045
1046
1047
1048
1049 public abstract TableInfoTable<TableInfo> getTableInfoTable();
1050
1051
1052
1053
1054 public abstract TableCategoryTable<TableCategory> getTableCategoryTable();
1055
1056
1057
1058
1059
1060 public abstract ColumnInfoTable<ColumnInfo> getColumnInfoTable();
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078 public abstract CapabilityTable<Capability> getCapabilityTable();
1079
1080
1081
1082
1083 public abstract UserTable<User> getUserTable();
1084
1085
1086
1087
1088 public abstract GroupTable<Group> getGroupTable();
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100 public abstract GroupMembershipTable<GroupMembership> getGroupMembershipTable();
1101
1102
1103
1104
1105
1106
1107 public abstract GroupCapabilityTable<GroupCapability> getGroupCapabilityTable();
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118 public abstract SettingTable<Setting> getSettingTable();
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131 public ResultSet sqlQuery(String sql) throws SQLPoemException {
1132 SessionToken token = PoemThread.sessionToken();
1133 token.transaction.writeDown();
1134 try {
1135 Statement s = token.transaction.getConnection().createStatement();
1136 token.toTidy().add(s);
1137 ResultSet rs = s.executeQuery(sql);
1138 token.toTidy().add(rs);
1139 if (logSQL())
1140 log(new SQLLogEvent(sql));
1141 incrementQueryCount(sql);
1142 return rs;
1143 }
1144 catch (SQLException e) {
1145 throw new ExecutingSQLPoemException(sql, e);
1146 }
1147 }
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168 public int sqlUpdate(String sql) throws SQLPoemException {
1169 SessionToken token = PoemThread.sessionToken();
1170 token.transaction.writeDown();
1171
1172 try {
1173 Statement s = token.transaction.getConnection().createStatement();
1174 token.toTidy().add(s);
1175 int n = s.executeUpdate(sql);
1176 if (logSQL())
1177 log(new SQLLogEvent(sql));
1178 incrementQueryCount(sql);
1179 return n;
1180 }
1181 catch (SQLException e) {
1182 throw dbms.exceptionForUpdate(null, sql,
1183 sql.indexOf("INSERT") >= 0 ||
1184 sql.indexOf("insert") >= 0,
1185 e);
1186 }
1187 }
1188
1189
1190
1191
1192 public User guestUser() {
1193 if (guest == null)
1194 guest = getUserTable().guestUser();
1195 return guest;
1196 }
1197
1198
1199
1200
1201 public User administratorUser() {
1202 if (administrator == null)
1203 administrator = getUserTable().administratorUser();
1204 return administrator;
1205 }
1206
1207
1208
1209
1210
1211
1212
1213
1214 public String givesCapabilitySQL(User user, Capability capability) {
1215
1216 return dbms.givesCapabilitySQL(user.troid(), capability.troid().toString());
1217 }
1218
1219
1220
1221
1222 private boolean dbGivesCapability(User user, Capability capability) {
1223
1224 String sql = givesCapabilitySQL(user, capability);
1225 ResultSet rs = null;
1226 try {
1227 rs = sqlQuery(sql);
1228 return rs.next();
1229 }
1230 catch (SQLPoemException e) {
1231 throw new UnexpectedExceptionPoemException(e);
1232 }
1233 catch (SQLException e) {
1234 throw new SQLSeriousPoemException(e, sql);
1235 }
1236 finally {
1237 try { if (rs != null) rs.close(); } catch (Exception e) {
1238 System.err.println("Cannot close resultset after exception.");
1239 }
1240 }
1241 }
1242
1243
1244
1245
1246
1247
1248
1249 public boolean hasCapability(User user, Capability capability) {
1250
1251 if (capability == null) return true;
1252
1253 return capabilityCache.hasCapability(user, capability);
1254 }
1255
1256
1257
1258
1259 public AccessToken guestAccessToken() {
1260 return getUserTable().guestUser();
1261 }
1262
1263
1264
1265
1266 public Capability administerCapability() {
1267 return getCapabilityTable().administer();
1268 }
1269
1270
1271
1272
1273
1274
1275
1276 public Capability getCanAdminister() {
1277 return canAdminister;
1278 }
1279
1280
1281
1282
1283
1284
1285 public void setCanAdminister(String capabilityName) {
1286 canAdminister = getCapabilityTable().ensure(capabilityName);
1287 }
1288
1289
1290
1291
1292
1293
1294
1295 public void setCanAdminister() {
1296 canAdminister = administerCapability();
1297 }
1298
1299
1300
1301
1302
1303
1304
1305
1306 public void trimCache(int maxSize) {
1307 for (Table<?> table : tables)
1308 table.trimCache(maxSize);
1309 }
1310
1311
1312
1313
1314 public void uncache() {
1315 for (int t = 0; t < tables.size(); ++t)
1316 tables.elementAt(t).uncache();
1317 }
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331 @SuppressWarnings({ "rawtypes", "unchecked" })
1332 public <P extends Persistent> Enumeration<P> referencesTo(final Persistent persistent) {
1333 return new FlattenedEnumeration<P>(
1334 new MappedEnumeration(tables()) {
1335 @Override
1336 public Object mapped(Object table) {
1337 return ((Table<P>)table).referencesTo(persistent);
1338 }
1339 });
1340 }
1341
1342
1343
1344
1345
1346 public List<Persistent> getReferencesTo(final Persistent persistent) {
1347 return EnumUtils.list(referencesTo(persistent));
1348 }
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359 public Enumeration<Column<?>> referencesTo(final Table<?> tableIn) {
1360 return new FlattenedEnumeration<Column<?>>(
1361 new MappedEnumeration<Enumeration<Column<?>>, Table<?>>(tables()) {
1362 public Enumeration<Column<?>> mapped(Table<?> table) {
1363 return table.referencesTo(tableIn);
1364 }
1365 });
1366 }
1367
1368
1369
1370
1371
1372 public List<Column<?>> getReferencesTo(final Table<?> table) {
1373 return EnumUtils.list(referencesTo(table));
1374 }
1375
1376
1377
1378
1379
1380 public void dumpCacheAnalysis() {
1381 for (Table<?> table : tables)
1382 table.dumpCacheAnalysis();
1383 }
1384
1385
1386
1387
1388 public void dump() {
1389 for (int t = 0; t < tables.size(); ++t) {
1390 System.out.println();
1391 tables.elementAt(t).dump();
1392 }
1393
1394 System.err.println("there are " + getTransactionsCount() + " transactions " +
1395 "of which " + getFreeTransactionsCount() + " are free");
1396 }
1397
1398
1399
1400
1401 public Dbms getDbms() {
1402 return dbms;
1403 }
1404
1405
1406
1407
1408
1409
1410 private void setDbms(Dbms aDbms) {
1411 dbms = aDbms;
1412 }
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425 public final String quotedName(String nameIn) {
1426 return getDbms().getQuotedName(nameIn);
1427 }
1428
1429
1430
1431
1432
1433
1434
1435 final SQLPoemType<?> defaultPoemTypeOfColumnMetaData(ResultSet md)
1436 throws SQLException {
1437 return getDbms().defaultPoemTypeOfColumnMetaData(md);
1438 }
1439
1440
1441
1442
1443
1444
1445
1446 public String toString() {
1447 if (connectionUrl == null)
1448 return "unconnected database";
1449 else
1450 return connectionUrl;
1451 }
1452
1453
1454
1455
1456 public Connection getCommittedConnection() {
1457 return committedConnection;
1458 }
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469 public boolean logSQL() {
1470 return logSQL;
1471 }
1472
1473
1474
1475
1476 public void setLogSQL(boolean value) {
1477 logSQL = value;
1478 }
1479
1480
1481
1482
1483 public boolean logCommits() {
1484 return logCommits;
1485 }
1486
1487
1488
1489
1490 public void setLogCommits(boolean value) {
1491 logCommits = value;
1492 }
1493
1494 void log(PoemLogEvent e) {
1495 System.err.println("---\n" + e.toString());
1496 }
1497
1498 void log(String s) {
1499 System.err.println(s);
1500 }
1501
1502 protected void beginStructuralModification() {
1503 beginExclusiveLock();
1504 }
1505
1506
1507
1508
1509 protected void endStructuralModification() {
1510 for (int t = 0; t < tables.size(); ++t)
1511 tables.elementAt(t).uncache();
1512 ++structureSerial;
1513 endExclusiveLock();
1514 }
1515
1516
1517
1518
1519 long structureSerial() {
1520 return structureSerial;
1521 }
1522
1523
1524
1525
1526
1527
1528 public int getQueryCount() {
1529 return queryCount;
1530 }
1531
1532
1533
1534
1535 public void incrementQueryCount(String sql) {
1536 lastQuery = sql;
1537 queryCount++;
1538 }
1539
1540
1541
1542
1543 public String getLastQuery() {
1544 return lastQuery;
1545 }
1546
1547
1548
1549
1550 public String getName() {
1551 return name;
1552 }
1553
1554
1555
1556
1557 public String getDisplayName() {
1558 if (displayName == null)
1559 return StringUtils.capitalised(getName());
1560 return displayName;
1561 }
1562
1563
1564
1565
1566 public void setDisplayName(String displayName) {
1567 this.displayName = displayName;
1568 }
1569
1570
1571
1572
1573
1574
1575
1576 public void modifyStructure(String sql)
1577 throws StructuralModificationFailedPoemException {
1578
1579
1580 if (PoemThread.inSession())
1581 PoemThread.commit();
1582
1583 try {
1584 Statement updateStatement = getCommittedConnection().createStatement();
1585 updateStatement.executeUpdate(sql);
1586 updateStatement.close();
1587 getCommittedConnection().commit();
1588 if (logCommits()) log(new CommitLogEvent(null));
1589 if (logSQL()) log(new StructuralModificationLogEvent(sql));
1590 incrementQueryCount(sql);
1591 }
1592 catch (SQLException e) {
1593 throw new StructuralModificationFailedPoemException(sql, e);
1594 }
1595 }
1596
1597
1598
1599
1600
1601 public class ConnectingException extends PoemException {
1602 private static final long serialVersionUID = 1L;
1603
1604
1605
1606
1607 public String getMessage() {
1608 return "Connection to the database is currently in progress; " +
1609 "please try again in a moment";
1610 }
1611 }
1612
1613 private class UserCapabilityCache {
1614 private Hashtable<Long, Boolean> userCapabilities = null;
1615 private long groupMembershipSerial;
1616 private long groupCapabilitySerial;
1617
1618 boolean hasCapability(User user, Capability capability) {
1619 PoemTransaction transaction = PoemThread.transaction();
1620 long currentGroupMembershipSerial =
1621 getGroupMembershipTable().serial(transaction);
1622 long currentGroupCapabilitySerial =
1623 getGroupCapabilityTable().serial(transaction);
1624
1625 if (userCapabilities == null ||
1626 groupMembershipSerial != currentGroupMembershipSerial ||
1627 groupCapabilitySerial != currentGroupCapabilitySerial) {
1628 userCapabilities = new Hashtable<Long, Boolean>();
1629 groupMembershipSerial = currentGroupMembershipSerial;
1630 groupCapabilitySerial = currentGroupCapabilitySerial;
1631 }
1632
1633 Long pair = new Long(
1634 (user.troid().longValue() << 32) | (capability.troid().longValue()));
1635 Boolean known = userCapabilities.get(pair);
1636
1637 if (known != null)
1638 return known.booleanValue();
1639 else {
1640 boolean does = dbGivesCapability(user, capability);
1641 userCapabilities.put(pair, does ? Boolean.TRUE : Boolean.FALSE);
1642 return does;
1643 }
1644 }
1645 }
1646
1647 }
1648