Source Code Cross Referenced for ConnectionTriggerManager.java in  » Database-DBMS » mckoi » com » mckoi » database » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database DBMS » mckoi » com.mckoi.database 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /**
002:         * com.mckoi.database.ConnectionTriggerManager  13 Mar 2003
003:         *
004:         * Mckoi SQL Database ( http://www.mckoi.com/database )
005:         * Copyright (C) 2000, 2001, 2002  Diehl and Associates, Inc.
006:         *
007:         * This program is free software; you can redistribute it and/or
008:         * modify it under the terms of the GNU General Public License
009:         * Version 2 as published by the Free Software Foundation.
010:         *
011:         * This program is distributed in the hope that it will be useful,
012:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
013:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014:         * GNU General Public License Version 2 for more details.
015:         *
016:         * You should have received a copy of the GNU General Public License
017:         * Version 2 along with this program; if not, write to the Free Software
018:         * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
019:         *
020:         * Change Log:
021:         * 
022:         * 
023:         */package com.mckoi.database;
024:
025:        import java.io.*;
026:        import java.util.ArrayList;
027:        import com.mckoi.debug.Lvl;
028:        import com.mckoi.util.IntegerVector;
029:        import com.mckoi.util.BigNumber;
030:
031:        /**
032:         * A trigger manager on a DatabaseConnection that maintains a list of all
033:         * triggers set in the database, and the types of triggers they are.  This
034:         * object is closely tied to a DatabaseConnection.
035:         * <p>
036:         * The trigger manager actually uses a trigger itself to maintain a list of
037:         * tables that have triggers, and the action to perform on the trigger.
038:         *
039:         * @author Tobias Downer
040:         */
041:
042:        public final class ConnectionTriggerManager {
043:
044:            /**
045:             * The DatabaseConnection.
046:             */
047:            private DatabaseConnection connection;
048:
049:            /**
050:             * The list of triggers currently in view.
051:             * (TriggerInfo)
052:             */
053:            private ArrayList triggers_active;
054:
055:            /**
056:             * If this is false then the list is not validated and must be refreshed
057:             * when we next access trigger information.
058:             */
059:            private boolean list_validated;
060:
061:            /**
062:             * True if the trigger table was modified during the last transaction.
063:             */
064:            private boolean trigger_modified;
065:
066:            /**
067:             * Constructs the manager.
068:             */
069:            ConnectionTriggerManager(DatabaseConnection connection) {
070:                this .connection = connection;
071:                this .triggers_active = new ArrayList();
072:                this .list_validated = false;
073:                this .trigger_modified = false;
074:                // Attach a commit trigger listener
075:                connection.attachTableBackedCache(new CTMBackedCache());
076:            }
077:
078:            /**
079:             * Returns a Table object that contains the trigger information with the
080:             * given name.  Returns an empty table if no trigger found.
081:             */
082:            private Table findTrigger(QueryContext context, DataTable table,
083:                    String schema, String name) {
084:                // Find all the trigger entries with this name
085:                Operator EQUALS = Operator.get("=");
086:
087:                Variable schemav = table.getResolvedVariable(0);
088:                Variable namev = table.getResolvedVariable(1);
089:
090:                Table t = table.simpleSelect(context, namev, EQUALS,
091:                        new Expression(TObject.stringVal(name)));
092:                return t.exhaustiveSelect(context, Expression.simple(schemav,
093:                        EQUALS, TObject.stringVal(schema)));
094:            }
095:
096:            /**
097:             * Creates a new trigger action on a stored procedure and makes the change
098:             * to the transaction of this DatabaseConnection.  If the connection is
099:             * committed then the trigger is made a perminant change to the database.
100:             *
101:             * @param schema the schema name of the trigger.
102:             * @param name the name of the trigger.
103:             * @param type the type of trigger.
104:             * @param procedure_name the name of the procedure to execute.
105:             * @param params any constant parameters for the triggering procedure.
106:             */
107:            public void createTableTrigger(String schema, String name,
108:                    int type, TableName on_table, String procedure_name,
109:                    TObject[] params) throws DatabaseException {
110:
111:                TableName trigger_table_name = new TableName(schema, name);
112:
113:                // Check this name is not reserved
114:                DatabaseConnection.checkAllowCreate(trigger_table_name);
115:
116:                // Before adding the trigger, make sure this name doesn't already resolve
117:                // to an object in the database with this schema/name.
118:                if (!connection.tableExists(trigger_table_name)) {
119:
120:                    // Encode the parameters
121:                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
122:                    try {
123:                        ObjectOutputStream ob_out = new ObjectOutputStream(bout);
124:                        ob_out.writeInt(1); // version
125:                        ob_out.writeObject(params);
126:                        ob_out.flush();
127:                    } catch (IOException e) {
128:                        throw new RuntimeException("IO Error: "
129:                                + e.getMessage());
130:                    }
131:                    byte[] encoded_params = bout.toByteArray();
132:
133:                    // Insert the entry into the trigger table,
134:                    DataTable table = connection
135:                            .getTable(Database.SYS_DATA_TRIGGER);
136:                    RowData row = new RowData(table);
137:                    row.setColumnDataFromTObject(0, TObject.stringVal(schema));
138:                    row.setColumnDataFromTObject(1, TObject.stringVal(name));
139:                    row.setColumnDataFromTObject(2, TObject.intVal(type));
140:                    row.setColumnDataFromTObject(3, TObject.stringVal("T:"
141:                            + on_table.toString()));
142:                    row.setColumnDataFromTObject(4, TObject
143:                            .stringVal(procedure_name));
144:                    row.setColumnDataFromTObject(5, TObject
145:                            .objectVal(encoded_params));
146:                    row.setColumnDataFromTObject(6, TObject
147:                            .stringVal(connection.getUser().getUserName()));
148:                    table.add(row);
149:
150:                    // Invalidate the list
151:                    invalidateTriggerList();
152:
153:                    // Notify that this database object has been successfully created.
154:                    connection.databaseObjectCreated(trigger_table_name);
155:
156:                    // Flag that this transaction modified the trigger table.
157:                    trigger_modified = true;
158:                } else {
159:                    throw new RuntimeException("Trigger name '" + schema + "."
160:                            + name + "' already in use.");
161:                }
162:            }
163:
164:            /**
165:             * Drops a trigger that has previously been defined.
166:             */
167:            public void dropTrigger(String schema, String name)
168:                    throws DatabaseException {
169:                QueryContext context = new DatabaseQueryContext(connection);
170:                DataTable table = connection
171:                        .getTable(Database.SYS_DATA_TRIGGER);
172:
173:                // Find the trigger
174:                Table t = findTrigger(context, table, schema, name);
175:
176:                if (t.getRowCount() == 0) {
177:                    throw new StatementException("Trigger '" + schema + "."
178:                            + name + "' not found.");
179:                } else if (t.getRowCount() > 1) {
180:                    throw new RuntimeException(
181:                            "Assertion failed: multiple entries for the same trigger name.");
182:                } else {
183:                    // Drop this trigger,
184:                    table.delete(t);
185:
186:                    // Notify that this database object has been successfully dropped.
187:                    connection
188:                            .databaseObjectDropped(new TableName(schema, name));
189:
190:                    // Flag that this transaction modified the trigger table.
191:                    trigger_modified = true;
192:                }
193:
194:            }
195:
196:            /**
197:             * Returns true if the trigger exists, false otherwise.
198:             */
199:            public boolean triggerExists(String schema, String name) {
200:                QueryContext context = new DatabaseQueryContext(connection);
201:                DataTable table = connection
202:                        .getTable(Database.SYS_DATA_TRIGGER);
203:
204:                // Find the trigger
205:                Table t = findTrigger(context, table, schema, name);
206:
207:                if (t.getRowCount() == 0) {
208:                    // Trigger wasn't found
209:                    return false;
210:                } else if (t.getRowCount() > 1) {
211:                    throw new RuntimeException(
212:                            "Assertion failed: multiple entries for the same trigger name.");
213:                } else {
214:                    // Trigger found
215:                    return true;
216:                }
217:            }
218:
219:            /**
220:             * Invalidates the trigger list causing the list to rebuild when a potential
221:             * triggering event next occurs.
222:             * <p>
223:             * NOTE: must only be called from the thread that owns the
224:             *   DatabaseConnection.
225:             */
226:            private void invalidateTriggerList() {
227:                list_validated = false;
228:                triggers_active.clear();
229:            }
230:
231:            /**
232:             * Build the trigger list if it is not validated.
233:             */
234:            private void buildTriggerList() {
235:                if (!list_validated) {
236:                    // Cache the trigger table
237:                    DataTable table = connection
238:                            .getTable(Database.SYS_DATA_TRIGGER);
239:                    RowEnumeration e = table.rowEnumeration();
240:
241:                    // For each row
242:                    while (e.hasMoreRows()) {
243:                        int row_index = e.nextRowIndex();
244:
245:                        TObject trig_schem = table
246:                                .getCellContents(0, row_index);
247:                        TObject trig_name = table.getCellContents(1, row_index);
248:                        TObject type = table.getCellContents(2, row_index);
249:                        TObject on_object = table.getCellContents(3, row_index);
250:                        TObject action = table.getCellContents(4, row_index);
251:                        TObject misc = table.getCellContents(5, row_index);
252:
253:                        TriggerInfo trigger_info = new TriggerInfo();
254:                        trigger_info.schema = trig_schem.getObject().toString();
255:                        trigger_info.name = trig_name.getObject().toString();
256:                        trigger_info.type = type.toBigNumber().intValue();
257:                        trigger_info.on_object = on_object.getObject()
258:                                .toString();
259:                        trigger_info.action = action.getObject().toString();
260:                        trigger_info.misc = misc;
261:
262:                        // Add to the list
263:                        triggers_active.add(trigger_info);
264:                    }
265:
266:                    list_validated = true;
267:                }
268:            }
269:
270:            /**
271:             * Performs any trigger action for this event.  For example, if we have it
272:             * setup so a trigger fires when there is an INSERT event on table x then
273:             * we perform the triggering procedure right here.
274:             */
275:            void performTriggerAction(TableModificationEvent evt) {
276:                // REINFORCED NOTE: The 'tableExists' call is REALLY important.  First it
277:                //   makes sure the transaction on the connection is established (it should
278:                //   be anyway if a trigger is firing), and it also makes sure the trigger
279:                //   table exists - which it may not be during database init.
280:                if (connection.tableExists(Database.SYS_DATA_TRIGGER)) {
281:                    // If the trigger list isn't built, then do so now
282:                    buildTriggerList();
283:
284:                    // On object value to test for,
285:                    TableName table_name = evt.getTableName();
286:                    String on_ob_test = "T:" + table_name.toString();
287:
288:                    // Search the triggers list for an event that matches this event
289:                    int sz = triggers_active.size();
290:                    for (int i = 0; i < sz; ++i) {
291:                        TriggerInfo t_info = (TriggerInfo) triggers_active
292:                                .get(i);
293:                        if (t_info.on_object.equals(on_ob_test)) {
294:                            // Table name matches
295:                            // Do the types match?  eg. before/after match, and
296:                            // insert/delete/update is being listened to.
297:                            if (evt.listenedBy(t_info.type)) {
298:                                // Type matches this trigger, so we need to fire it
299:                                // Parse the action string
300:                                String action = t_info.action;
301:                                // Get the procedure name to fire (qualify it against the schema
302:                                // of the table being fired).
303:                                ProcedureName procedure_name = ProcedureName
304:                                        .qualify(table_name.getSchema(), action);
305:                                // Set up OLD and NEW tables
306:
307:                                // Record the old table state
308:                                DatabaseConnection.OldNewTableState current_state = connection
309:                                        .getOldNewTableState();
310:
311:                                // Set the new table state
312:                                // If an INSERT event then we setup NEW to be the row being inserted
313:                                // If an DELETE event then we setup OLD to be the row being deleted
314:                                // If an UPDATE event then we setup NEW to be the row after the
315:                                // update, and OLD to be the row before the update.
316:                                connection
317:                                        .setOldNewTableState(new DatabaseConnection.OldNewTableState(
318:                                                table_name, evt.getRowIndex(),
319:                                                evt.getRowData(), evt
320:                                                        .isBefore()));
321:
322:                                try {
323:                                    // Invoke the procedure (no arguments)
324:                                    connection.getProcedureManager()
325:                                            .invokeProcedure(procedure_name,
326:                                                    new TObject[0]);
327:                                } finally {
328:                                    // Reset the OLD and NEW tables to previous values
329:                                    connection
330:                                            .setOldNewTableState(current_state);
331:                                }
332:
333:                            }
334:
335:                        }
336:
337:                    } // for each trigger
338:
339:                }
340:
341:            }
342:
343:            /**
344:             * Returns an InternalTableInfo object used to model the list of triggers
345:             * that are accessible within the given Transaction object.  This is used to
346:             * model all triggers that have been defined as tables.
347:             */
348:            static InternalTableInfo createInternalTableInfo(
349:                    Transaction transaction) {
350:                return new TriggerInternalTableInfo(transaction);
351:            }
352:
353:            // ---------- Inner classes ----------
354:
355:            /**
356:             * A TableBackedCache that manages the list of connection level triggers that
357:             * are currently active on this connection.
358:             */
359:            private class CTMBackedCache extends TableBackedCache {
360:
361:                /**
362:                 * Constructor.
363:                 */
364:                public CTMBackedCache() {
365:                    super (Database.SYS_DATA_TRIGGER);
366:                }
367:
368:                public void purgeCacheOfInvalidatedEntries(
369:                        IntegerVector added_rows, IntegerVector removed_rows) {
370:                    // Note that this is called when a transaction is started or stopped.
371:
372:                    // If the trigger table was modified, we need to invalidate the trigger
373:                    // list.  This covers the case when we rollback a trigger table change
374:                    if (trigger_modified) {
375:                        invalidateTriggerList();
376:                        trigger_modified = false;
377:                    }
378:                    // If any data has been committed removed then completely flush the
379:                    // cache.
380:                    else if ((removed_rows != null && removed_rows.size() > 0)
381:                            || (added_rows != null && added_rows.size() > 0)) {
382:                        invalidateTriggerList();
383:                    }
384:                }
385:
386:            }
387:
388:            /**
389:             * Container class for all trigger actions defined on the database.
390:             */
391:            private class TriggerInfo {
392:                String schema;
393:                String name;
394:                int type;
395:                String on_object;
396:                String action;
397:                TObject misc;
398:            }
399:
400:            /**
401:             * An object that models the list of triggers as table objects in a
402:             * transaction.
403:             */
404:            private static class TriggerInternalTableInfo extends
405:                    AbstractInternalTableInfo2 {
406:
407:                TriggerInternalTableInfo(Transaction transaction) {
408:                    super (transaction, Database.SYS_DATA_TRIGGER);
409:                }
410:
411:                private static DataTableDef createDataTableDef(String schema,
412:                        String name) {
413:                    // Create the DataTableDef that describes this entry
414:                    DataTableDef def = new DataTableDef();
415:                    def.setTableName(new TableName(schema, name));
416:
417:                    // Add column definitions
418:                    def.addColumn(DataTableColumnDef
419:                            .createNumericColumn("type"));
420:                    def.addColumn(DataTableColumnDef
421:                            .createStringColumn("on_object"));
422:                    def.addColumn(DataTableColumnDef
423:                            .createStringColumn("procedure_name"));
424:                    def.addColumn(DataTableColumnDef
425:                            .createStringColumn("param_args"));
426:                    def.addColumn(DataTableColumnDef
427:                            .createStringColumn("owner"));
428:
429:                    // Set to immutable
430:                    def.setImmutable();
431:
432:                    // Return the data table def
433:                    return def;
434:                }
435:
436:                public String getTableType(int i) {
437:                    return "TRIGGER";
438:                }
439:
440:                public DataTableDef getDataTableDef(int i) {
441:                    TableName table_name = getTableName(i);
442:                    return createDataTableDef(table_name.getSchema(),
443:                            table_name.getName());
444:                }
445:
446:                public MutableTableDataSource createInternalTable(int index) {
447:                    MutableTableDataSource table = transaction
448:                            .getTable(Database.SYS_DATA_TRIGGER);
449:                    RowEnumeration row_e = table.rowEnumeration();
450:                    int p = 0;
451:                    int i;
452:                    int row_i = -1;
453:                    while (row_e.hasMoreRows()) {
454:                        i = row_e.nextRowIndex();
455:                        if (p == index) {
456:                            row_i = i;
457:                        } else {
458:                            ++p;
459:                        }
460:                    }
461:                    if (p == index) {
462:                        String schema = table.getCellContents(0, row_i)
463:                                .getObject().toString();
464:                        String name = table.getCellContents(1, row_i)
465:                                .getObject().toString();
466:
467:                        final DataTableDef table_def = createDataTableDef(
468:                                schema, name);
469:                        final TObject type = table.getCellContents(2, row_i);
470:                        final TObject on_object = table.getCellContents(3,
471:                                row_i);
472:                        final TObject procedure_name = table.getCellContents(4,
473:                                row_i);
474:                        final TObject param_args = table.getCellContents(5,
475:                                row_i);
476:                        final TObject owner = table.getCellContents(6, row_i);
477:
478:                        // Implementation of MutableTableDataSource that describes this
479:                        // trigger.
480:                        return new GTDataSource(transaction.getSystem()) {
481:                            public DataTableDef getDataTableDef() {
482:                                return table_def;
483:                            }
484:
485:                            public int getRowCount() {
486:                                return 1;
487:                            }
488:
489:                            public TObject getCellContents(int col, int row) {
490:                                switch (col) {
491:                                case 0:
492:                                    return type;
493:                                case 1:
494:                                    return on_object;
495:                                case 2:
496:                                    return procedure_name;
497:                                case 3:
498:                                    return param_args;
499:                                case 4:
500:                                    return owner;
501:                                default:
502:                                    throw new RuntimeException(
503:                                            "Column out of bounds.");
504:                                }
505:                            }
506:                        };
507:
508:                    } else {
509:                        throw new RuntimeException("Index out of bounds.");
510:                    }
511:
512:                }
513:
514:            }
515:
516:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.