KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > amber > table > Table


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.amber.table;
31
32 import com.caucho.amber.AmberRuntimeException;
33 import com.caucho.amber.entity.AmberCompletion;
34 import com.caucho.amber.entity.Entity;
35 import com.caucho.amber.entity.EntityListener;
36 import com.caucho.amber.entity.TableInvalidateCompletion;
37 import com.caucho.amber.manager.AmberConnection;
38 import com.caucho.amber.manager.AmberPersistenceUnit;
39 import com.caucho.amber.type.RelatedType;
40 import com.caucho.amber.type.Type;
41 import com.caucho.config.ConfigException;
42 import com.caucho.config.LineConfigException;
43 import com.caucho.util.CharBuffer;
44 import com.caucho.util.L10N;
45
46 import javax.sql.DataSource JavaDoc;
47 import java.sql.Connection JavaDoc;
48 import java.sql.ResultSet JavaDoc;
49 import java.sql.SQLException JavaDoc;
50 import java.sql.Statement JavaDoc;
51 import java.util.ArrayList JavaDoc;
52 import java.util.Collections JavaDoc;
53
54 /**
55  * Representation of a database table.
56  */

57 public class Table {
58   private static final L10N L = new L10N(Table.class);
59
60   private String JavaDoc _name;
61
62   private String JavaDoc _configLocation;
63
64   private AmberPersistenceUnit _manager;
65
66   // The entity type is used to generate primary keys for cascade deletes
67
private RelatedType _type;
68
69   private ArrayList JavaDoc<Column> _columns = new ArrayList JavaDoc<Column>();
70
71   private ArrayList JavaDoc<LinkColumns> _incomingLinks = new ArrayList JavaDoc<LinkColumns>();
72   private ArrayList JavaDoc<LinkColumns> _outgoingLinks = new ArrayList JavaDoc<LinkColumns>();
73
74   private ArrayList JavaDoc<Column> _idColumns = new ArrayList JavaDoc<Column>();
75   private LinkColumns _dependentIdLink;
76
77   private boolean _isReadOnly;
78   private long _cacheTimeout = 250;
79
80   private ArrayList JavaDoc<EntityListener> _entityListeners
81     = new ArrayList JavaDoc<EntityListener>();
82
83   private TableInvalidateCompletion _invalidateCompletion;
84
85   public Table(AmberPersistenceUnit manager, String JavaDoc name)
86   {
87     _manager = manager;
88     _name = name;
89   }
90
91   /**
92    * Gets the sql table name.
93    */

94   public String JavaDoc getName()
95   {
96     return _name;
97   }
98
99   /**
100    * Sets the config location.
101    */

102   public void setConfigLocation(String JavaDoc location)
103   {
104     _configLocation = location;
105   }
106
107   /**
108    * Returns the location.
109    */

110   public String JavaDoc getLocation()
111   {
112     return _configLocation;
113   }
114
115   /**
116    * Returns the amber manager.
117    */

118   public AmberPersistenceUnit getAmberManager()
119   {
120     return _manager;
121   }
122
123   /**
124    * Sets the entity type.
125    */

126   public void setType(RelatedType type)
127   {
128     if (_type == null)
129       _type = type;
130   }
131
132   /**
133    * Gets the entity type.
134    */

135   public RelatedType getType()
136   {
137     return _type;
138   }
139
140   /**
141    * Returns true if read-only
142    */

143   public boolean isReadOnly()
144   {
145     return _isReadOnly;
146   }
147
148   /**
149    * Sets true if read-only
150    */

151   public void setReadOnly(boolean isReadOnly)
152   {
153     _isReadOnly = isReadOnly;
154   }
155
156   /**
157    * Returns the cache timeout.
158    */

159   public long getCacheTimeout()
160   {
161     return _cacheTimeout;
162   }
163
164   /**
165    * Sets the cache timeout.
166    */

167   public void setCacheTimeout(long timeout)
168   {
169     _cacheTimeout = timeout;
170   }
171
172   /**
173    * Creates a column.
174    */

175   public Column createColumn(String JavaDoc name, Type type)
176   {
177     for (int i = 0; i < _columns.size(); i++) {
178       Column oldColumn = _columns.get(i);
179
180       if (oldColumn.getName().equals(name))
181         return oldColumn;
182     }
183
184     Column column = new Column(this, name, type);
185
186     _columns.add(column);
187     Collections.sort(_columns, new ColumnCompare());
188
189     return column;
190   }
191
192   /**
193    * Creates a foreign column.
194    */

195   public ForeignColumn createForeignColumn(String JavaDoc name, Column key)
196   {
197     for (int i = 0; i < _columns.size(); i++) {
198       Column oldColumn = _columns.get(i);
199
200       if (! oldColumn.getName().equals(name)) {
201       }
202       else if (oldColumn instanceof ForeignColumn) {
203         // XXX: check type
204
return (ForeignColumn) oldColumn;
205       }
206       else {
207         // XXX: copy props(?)
208

209         ForeignColumn column = new ForeignColumn(this, name, key);
210         _columns.set(i, column);
211         return column;
212       }
213     }
214
215     ForeignColumn column = new ForeignColumn(this, name, key);
216
217     _columns.add(column);
218     Collections.sort(_columns, new ColumnCompare());
219
220     return column;
221   }
222
223   /**
224    * Adds a column.
225    */

226   public Column addColumn(Column column)
227   {
228     for (int i = 0; i < _columns.size(); i++) {
229       Column oldColumn = _columns.get(i);
230
231       if (! oldColumn.getName().equals(column.getName())) {
232       }
233       else if (oldColumn instanceof ForeignColumn)
234         return oldColumn;
235       else if (column instanceof ForeignColumn) {
236         _columns.set(i, column);
237         return column;
238       }
239       else
240         return oldColumn;
241     }
242
243     _columns.add(column);
244     Collections.sort(_columns, new ColumnCompare());
245
246     return column;
247   }
248
249   /**
250    * Returns the columns.
251    */

252   public ArrayList JavaDoc<Column> getColumns()
253   {
254     return _columns;
255   }
256
257   /**
258    * Remove a given column.
259    */

260   public boolean removeColumn(Column column)
261   {
262     return _columns.remove(column);
263   }
264
265   /**
266    * Adds an incoming link.
267    */

268   void addIncomingLink(LinkColumns link)
269   {
270     assert(! _incomingLinks.contains(link));
271
272     _incomingLinks.add(link);
273   }
274
275   /**
276    * Adds an outgoing link.
277    */

278   void addOutgoingLink(LinkColumns link)
279   {
280     assert(! _outgoingLinks.contains(link));
281
282     _outgoingLinks.add(link);
283   }
284
285   /**
286    * Adds an id column.
287    */

288   public void addIdColumn(Column column)
289   {
290     _idColumns.add(column);
291   }
292
293   /**
294    * Returns the id columns.
295    */

296   public ArrayList JavaDoc<Column> getIdColumns()
297   {
298     return _idColumns;
299   }
300
301   /**
302    * Sets the id link for a dependent table.
303    */

304   public void setDependentIdLink(LinkColumns link)
305   {
306     _dependentIdLink = link;
307   }
308
309   /**
310    * Gets the id link for a dependent table.
311    */

312   public LinkColumns getDependentIdLink()
313   {
314     return _dependentIdLink;
315   }
316
317   /**
318    * Creates the table if missing.
319    */

320   public void createDatabaseTable(AmberPersistenceUnit amberPersistenceUnit)
321     throws ConfigException
322   {
323     try {
324       DataSource JavaDoc ds = amberPersistenceUnit.getDataSource();
325       Connection conn = ds.getConnection();
326       try {
327         Statement JavaDoc stmt = conn.createStatement();
328
329         try {
330           // If the table exists, return
331

332           String JavaDoc sql = "select 1 from " + getName() + " o where 1=0";
333
334           ResultSet JavaDoc rs = stmt.executeQuery(sql);
335           rs.close();
336           return;
337         } catch (SQLException JavaDoc e) {
338         }
339
340         String JavaDoc createSQL = generateCreateTableSQL(amberPersistenceUnit);
341
342         stmt.executeUpdate(createSQL);
343
344         stmt.close();
345       } finally {
346         conn.close();
347       }
348     } catch (Exception JavaDoc e) {
349       throw error(e);
350     }
351   }
352
353   /**
354    * Generates the SQL to create the table.
355    */

356   private String JavaDoc generateCreateTableSQL(AmberPersistenceUnit amberPersistenceUnit)
357   {
358     CharBuffer cb = new CharBuffer();
359
360     cb.append("create table " + getName() + " (");
361
362     boolean hasColumn = false;
363     for (Column column : _columns) {
364       String JavaDoc columnSQL = column.generateCreateTableSQL(amberPersistenceUnit);
365
366       if (columnSQL == null) {
367       }
368       else if (! hasColumn) {
369         hasColumn = true;
370         cb.append("\n " + columnSQL);
371       }
372       else {
373         cb.append(",\n " + columnSQL);
374       }
375     }
376
377     cb.append("\n)");
378
379     return cb.close();
380   }
381
382   /**
383    * Creates the table if missing.
384    */

385   public void validateDatabaseTable(AmberPersistenceUnit amberPersistenceUnit)
386     throws ConfigException
387   {
388     try {
389       DataSource JavaDoc ds = amberPersistenceUnit.getDataSource();
390       Connection conn = ds.getConnection();
391       try {
392         Statement JavaDoc stmt = conn.createStatement();
393
394         try {
395           // If the table exists, return
396

397           String JavaDoc sql = "select 1 from " + getName() + " o where 1=0";
398
399           ResultSet JavaDoc rs = stmt.executeQuery(sql);
400           rs.close();
401         } catch (SQLException JavaDoc e) {
402           throw error(L.l("'{0}' is not a valid database table. Either the table needs to be created or the create-database-tables attribute must be set.\n\n{1}",
403                           getName(), e.toString()), e);
404         }
405       } finally {
406         conn.close();
407       }
408
409       for (Column column : _columns) {
410         column.validateDatabase(amberPersistenceUnit);
411       }
412     } catch (ConfigException e) {
413       if (_type != null)
414         _type.setConfigException(e);
415
416       throw e;
417     } catch (Exception JavaDoc e) {
418       if (_type != null)
419         _type.setConfigException(e);
420
421       throw error(e);
422     }
423   }
424
425   /**
426    * Returns the table's invalidation.
427    */

428   public AmberCompletion getInvalidateCompletion()
429   {
430     if (_invalidateCompletion == null)
431       _invalidateCompletion = new TableInvalidateCompletion(getName());
432
433     return _invalidateCompletion;
434   }
435
436   /**
437    * Returns the table's invalidation.
438    */

439   public AmberCompletion getUpdateCompletion()
440   {
441     return getInvalidateCompletion();
442   }
443
444   /**
445    * Returns the table's invalidation.
446    */

447   public AmberCompletion getDeleteCompletion()
448   {
449     return getInvalidateCompletion();
450   }
451
452   /**
453    * Adds a listener for create/delete events
454    */

455   public void addEntityListener(EntityListener listener)
456   {
457     if (! _entityListeners.contains(listener))
458       _entityListeners.add(listener);
459   }
460
461   /**
462    * Returns true if there are any listeners.
463    */

464   public boolean hasListeners()
465   {
466     return _entityListeners.size() > 0;
467   }
468
469   /**
470    * Returns true if any deletes of this object are cascaded.
471    */

472   public boolean isCascadeDelete()
473   {
474     // check if any of the incoming links have a target cascade delete
475
for (int i = 0; i < _incomingLinks.size(); i++) {
476       LinkColumns link = _incomingLinks.get(i);
477
478       if (link.isSourceCascadeDelete())
479         return true;
480     }
481
482     // check if any of the outgoing links have a source cascade delete
483
for (int i = 0; i < _outgoingLinks.size(); i++) {
484       LinkColumns link = _outgoingLinks.get(i);
485
486       if (link.isTargetCascadeDelete())
487         return true;
488     }
489
490     return false;
491   }
492
493   /**
494    * Called before the entity is deleted.
495    */

496   public void beforeEntityDelete(AmberConnection aConn, Entity entity)
497   {
498     try {
499       for (int i = 0; i < _entityListeners.size(); i++) {
500         EntityListener listener = _entityListeners.get(i);
501
502         listener.beforeEntityDelete(aConn, entity);
503       }
504       // getHome().completeDelete(aConn, key);
505

506       for (int i = 0; i < _incomingLinks.size(); i++) {
507         LinkColumns link = _incomingLinks.get(i);
508
509         link.beforeTargetDelete(aConn, entity);
510       }
511
512       aConn.addCompletion(getDeleteCompletion());
513     } catch (RuntimeException JavaDoc e) {
514       throw e;
515     } catch (Exception JavaDoc e) {
516       throw new AmberRuntimeException(e);
517     }
518   }
519
520   protected ConfigException error(String JavaDoc msg, Throwable JavaDoc e)
521   {
522     if (_configLocation != null)
523       return new LineConfigException(_configLocation + msg, e);
524     else
525       return new ConfigException(msg, e);
526   }
527
528   protected ConfigException error(Throwable JavaDoc e)
529   {
530     if (_configLocation != null)
531       return new LineConfigException(_configLocation + e.getMessage(), e);
532     else
533       return new ConfigException(e);
534   }
535
536   /**
537    * Printable version of the entity.
538    */

539   public String JavaDoc toString()
540   {
541     return "Table[" + getName() + "]";
542   }
543 }
544
Popular Tags