KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > cmp > jdbc > metadata > JDBCRelationMetaData


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb.plugins.cmp.jdbc.metadata;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import javax.ejb.EJBException JavaDoc;
27 import javax.naming.InitialContext JavaDoc;
28 import javax.naming.NamingException JavaDoc;
29 import javax.sql.DataSource JavaDoc;
30 import org.jboss.deployment.DeploymentException;
31 import org.jboss.metadata.MetaData;
32 import org.jboss.metadata.RelationMetaData;
33 import org.jboss.metadata.RelationshipRoleMetaData;
34 import org.w3c.dom.Element JavaDoc;
35
36 /**
37  * This class represents one ejb-relation element in the ejb-jar.xml file. Most
38  * properties of this class are immutable. The mutable properties have set
39  * methods.
40  *
41  * @author <a HREF="mailto:dain@daingroup.com">Dain Sundstrom </a>
42  * @author <a HREF="mailto:heiko.rupp@cellent.de">Heiko W. Rupp </a>
43  * @version $Revision: 41672 $
44  */

45 public final class JDBCRelationMetaData
46 {
47     private final static int TABLE = 1;
48
49     private final static int FOREIGN_KEY = 2;
50
51     /** Name of the relation. Loaded from the ejb-relation-name element. */
52     private final String JavaDoc relationName;
53
54     /**
55      * The left jdbc relationship role. Loaded from an ejb-relationship-role.
56      * Left/right assignment is completely arbitrary.
57      */

58     private final JDBCRelationshipRoleMetaData left;
59
60     /**
61      * The right relationship role. Loaded from an ejb-relationship-role.
62      * Left/right assignment is completely arbitrary.
63      */

64     private final JDBCRelationshipRoleMetaData right;
65
66     /**
67      * The mapping style for this relation (i.e., TABLE or FOREIGN_KEY).
68      */

69     private final int mappingStyle;
70
71     /** data source name in jndi */
72     private final String JavaDoc dataSourceName;
73
74    /** datasource type mapping name is defined in the deployment descriptor */
75    private final String JavaDoc datasourceMappingName;
76
77     /** This is a cache of the datasource object. */
78     private transient DataSource JavaDoc dataSource;
79
80     /** type mapping used for the relation table */
81     private final JDBCTypeMappingMetaData datasourceMapping;
82
83     /** the name of the table to use for this bean */
84     private final String JavaDoc tableName;
85
86     /** is table created */
87     private boolean tableCreated;
88
89     /** is table dropped */
90     private boolean tableDropped;
91
92     /** should we create the table when deployed */
93     private final boolean createTable;
94
95     /** should we drop the table when deployed */
96     private final boolean removeTable;
97
98     /** should we alter the table when deployed */
99     private final boolean alterTable;
100
101     /**
102      * What commands should be issued directly after creation of a table?
103      */

104     private final ArrayList JavaDoc tablePostCreateCmd;
105
106     /** should we use 'SELECT ... FOR UPDATE' syntax? */
107     private final boolean rowLocking;
108
109     /** should the table have a primary key constraint? */
110     private final boolean primaryKeyConstraint;
111
112     /** is the relationship read-only? */
113     private final boolean readOnly;
114
115     /** how long is read valid */
116     private final int readTimeOut;
117
118     /**
119      * Constructs jdbc relation meta data with the data from the relation
120      * metadata loaded from the ejb-jar.xml file.
121      *
122      * @param jdbcApplication used to retrieve the entities of this relation
123      * @param relationMetaData relation meta data loaded from the ejb-jar.xml
124      * file
125      */

126     public JDBCRelationMetaData(JDBCApplicationMetaData jdbcApplication, RelationMetaData relationMetaData)
127             throws DeploymentException
128     {
129
130         relationName = relationMetaData.getRelationName();
131
132         RelationshipRoleMetaData leftRole = relationMetaData.getLeftRelationshipRole();
133         RelationshipRoleMetaData rightRole = relationMetaData.getRightRelationshipRole();
134
135         // set the default mapping style
136
if (leftRole.isMultiplicityMany() && rightRole.isMultiplicityMany())
137         {
138             mappingStyle = TABLE;
139         }
140         else
141         {
142             mappingStyle = FOREIGN_KEY;
143         }
144
145         dataSourceName = null;
146       datasourceMappingName = null;
147         datasourceMapping = null;
148         createTable = false;
149         removeTable = false;
150         alterTable = false;
151         rowLocking = false;
152         primaryKeyConstraint = false;
153         readOnly = false;
154         readTimeOut = -1;
155
156         left = new JDBCRelationshipRoleMetaData(this, jdbcApplication, leftRole);
157
158         right = new JDBCRelationshipRoleMetaData(this, jdbcApplication, rightRole);
159         left.init(right);
160         right.init(left);
161
162         if (mappingStyle == TABLE)
163         {
164             tableName = createDefaultTableName();
165             tablePostCreateCmd = getDefaultTablePostCreateCmd();
166         }
167         else
168         {
169             tableName = null;
170             tablePostCreateCmd = null;
171         }
172     }
173
174     /**
175      * Constructs relation meta data with the data contained in the ejb-relation
176      * element or the defaults element from a jbosscmp-jdbc xml file. Optional
177      * values of the xml element that are not present are loaded from the
178      * defaultValues parameter.
179      *
180      * @param jdbcApplication used to retrieve type mappings in table mapping
181      * style
182      * @param element the xml Element which contains the metadata about this
183      * relation
184      * @param defaultValues the JDBCApplicationMetaData which contains the
185      * values for optional elements of the element
186      * @throws DeploymentException if the xml element is not semantically
187      * correct
188      */

189     public JDBCRelationMetaData(JDBCApplicationMetaData jdbcApplication, Element JavaDoc element,
190             JDBCRelationMetaData defaultValues) throws DeploymentException
191     {
192
193         relationName = defaultValues.getRelationName();
194         mappingStyle = loadMappingStyle(element, defaultValues);
195
196         // read-only
197
String JavaDoc readOnlyString = MetaData.getOptionalChildContent(element, "read-only");
198         if (readOnlyString != null)
199         {
200             readOnly = Boolean.valueOf(readOnlyString).booleanValue();
201         }
202         else
203         {
204             readOnly = defaultValues.isReadOnly();
205         }
206
207         // read-time-out
208
String JavaDoc readTimeOutString = MetaData.getOptionalChildContent(element, "read-time-out");
209         if (readTimeOutString != null)
210         {
211             try
212             {
213                 readTimeOut = Integer.parseInt(readTimeOutString);
214             }
215             catch (NumberFormatException JavaDoc e)
216             {
217                 throw new DeploymentException("Invalid number format in " + "read-time-out '" + readTimeOutString + "': "
218                         + e);
219             }
220         }
221         else
222         {
223             readTimeOut = defaultValues.getReadTimeOut();
224         }
225
226         //
227
// Load all of the table options. defaults and relation-table-mapping
228
// will have these elements, and foreign-key will get the default values.
229
//
230
Element JavaDoc mappingElement = getMappingElement(element);
231
232         // datasource name
233
String JavaDoc dataSourceNameString = MetaData.getOptionalChildContent(mappingElement, "datasource");
234         if (dataSourceNameString != null)
235          dataSourceName = dataSourceNameString;
236         else
237             dataSourceName = defaultValues.getDataSourceName();
238
239         // get the type mapping for this datasource (optional, but always
240
// set in standardjbosscmp-jdbc.xml)
241
String JavaDoc datasourceMappingString = MetaData.getOptionalChildContent(mappingElement, "datasource-mapping");
242         if (datasourceMappingString != null)
243         {
244          datasourceMappingName = datasourceMappingString;
245             datasourceMapping = jdbcApplication.getTypeMappingByName(datasourceMappingString);
246             if (datasourceMapping == null)
247             {
248                 throw new DeploymentException("Error in jbosscmp-jdbc.xml : " + "datasource-mapping "
249                         + datasourceMappingString + " not found");
250             }
251         }
252         else if(defaultValues.datasourceMappingName != null && defaultValues.getTypeMapping() != null)
253         {
254          datasourceMappingName = null;
255             datasourceMapping = defaultValues.getTypeMapping();
256         }
257       else
258       {
259          datasourceMappingName = null;
260          datasourceMapping = JDBCEntityMetaData.obtainTypeMappingFromLibrary(dataSourceName);
261       }
262
263         // get table name
264
String JavaDoc tableNameString = MetaData.getOptionalChildContent(mappingElement, "table-name");
265         if (tableNameString == null)
266         {
267             tableNameString = defaultValues.getDefaultTableName();
268             if (tableNameString == null)
269             {
270                 // use defaultValues to create default, because left/right
271
// have not been assigned yet, and values used to generate
272
// default table name never change
273
tableNameString = defaultValues.createDefaultTableName();
274             }
275         }
276         tableName = tableNameString;
277
278         // create table? If not provided, keep default.
279
String JavaDoc createString = MetaData.getOptionalChildContent(mappingElement, "create-table");
280         if (createString != null)
281         {
282             createTable = Boolean.valueOf(createString).booleanValue();
283         }
284         else
285         {
286             createTable = defaultValues.getCreateTable();
287         }
288
289         // remove table? If not provided, keep default.
290
String JavaDoc removeString = MetaData.getOptionalChildContent(mappingElement, "remove-table");
291         if (removeString != null)
292         {
293             removeTable = Boolean.valueOf(removeString).booleanValue();
294         }
295         else
296         {
297             removeTable = defaultValues.getRemoveTable();
298         }
299
300       // post-table-create commands
301
Element JavaDoc posttc = MetaData.getOptionalChild(mappingElement, "post-table-create");
302       if (posttc != null)
303       {
304          Iterator JavaDoc it = MetaData.getChildrenByTagName(posttc, "sql-statement");
305          tablePostCreateCmd = new ArrayList JavaDoc();
306          while (it.hasNext())
307          {
308             Element JavaDoc etmp = (Element JavaDoc) it.next();
309             tablePostCreateCmd.add(MetaData.getElementContent(etmp));
310          }
311       }
312       else
313       {
314          tablePostCreateCmd = defaultValues.getDefaultTablePostCreateCmd();
315       }
316
317         // alter table? If not provided, keep default.
318
String JavaDoc alterString = MetaData.getOptionalChildContent(mappingElement, "alter-table");
319         if (alterString != null)
320         {
321             alterTable = Boolean.valueOf(alterString).booleanValue();
322         }
323         else
324         {
325             alterTable = defaultValues.getAlterTable();
326         }
327
328         // select for update
329
String JavaDoc sForUpString = MetaData.getOptionalChildContent(mappingElement, "row-locking");
330         if (sForUpString != null)
331         {
332             rowLocking = !isReadOnly() && (Boolean.valueOf(sForUpString).booleanValue());
333         }
334         else
335         {
336             rowLocking = defaultValues.hasRowLocking();
337         }
338
339         // primary key constraint? If not provided, keep default.
340
String JavaDoc pkString = MetaData.getOptionalChildContent(mappingElement, "pk-constraint");
341         if (pkString != null)
342         {
343             primaryKeyConstraint = Boolean.valueOf(pkString).booleanValue();
344         }
345         else
346         {
347             primaryKeyConstraint = defaultValues.hasPrimaryKeyConstraint();
348         }
349
350         //
351
// load metadata for each specified role
352
//
353
JDBCRelationshipRoleMetaData defaultLeft = defaultValues.getLeftRelationshipRole();
354         JDBCRelationshipRoleMetaData defaultRight = defaultValues.getRightRelationshipRole();
355
356         if (!MetaData.getChildrenByTagName(element, "ejb-relationship-role").hasNext())
357         {
358
359             // no roles specified use the defaults
360
left = new JDBCRelationshipRoleMetaData(this, jdbcApplication, element, defaultLeft);
361
362             right = new JDBCRelationshipRoleMetaData(this, jdbcApplication, element, defaultRight);
363
364             left.init(right);
365             right.init(left);
366         }
367         else
368         {
369             Element JavaDoc leftElement = getEJBRelationshipRoleElement(element, defaultLeft);
370             left = new JDBCRelationshipRoleMetaData(this, jdbcApplication, leftElement, defaultLeft);
371
372             Element JavaDoc rightElement = getEJBRelationshipRoleElement(element, defaultRight);
373             right = new JDBCRelationshipRoleMetaData(this, jdbcApplication, rightElement, defaultRight);
374
375             left.init(right, leftElement);
376             right.init(left, rightElement);
377         }
378
379         // at least one side of a fk relation must have keys
380
if (isForeignKeyMappingStyle() && left.getKeyFields().isEmpty() && right.getKeyFields().isEmpty())
381         {
382             throw new DeploymentException("Atleast one role of a foreign-key "
383                     + "mapped relationship must have key fields " + "(or <primkey-field> is missing from ejb-jar.xml): "
384                     + "ejb-relation-name=" + relationName);
385         }
386
387         // both sides of a table relation must have keys
388
if (isTableMappingStyle() && (left.getKeyFields().isEmpty() || right.getKeyFields().isEmpty()))
389         {
390             throw new DeploymentException("Both roles of a relation-table " + "mapped relationship must have key fields: "
391                     + "ejb-relation-name=" + relationName);
392         }
393     }
394
395     private int loadMappingStyle(Element JavaDoc element, JDBCRelationMetaData defaultValues) throws DeploymentException
396     {
397
398         // if defaults check for preferred-relation-mapping
399
if ("defaults".equals(element.getTagName()))
400         {
401             // set mapping style based on preferred-relation-mapping (if possible)
402
String JavaDoc perferredRelationMapping = MetaData.getOptionalChildContent(element, "preferred-relation-mapping");
403
404             if ("relation-table".equals(perferredRelationMapping) || defaultValues.isManyToMany())
405             {
406                 return TABLE;
407             }
408             else
409             {
410                 return FOREIGN_KEY;
411             }
412         }
413
414         // check for table mapping style
415
if (MetaData.getOptionalChild(element, "relation-table-mapping") != null)
416         {
417             return TABLE;
418         }
419
420         // check for foreign-key mapping style
421
if (MetaData.getOptionalChild(element, "foreign-key-mapping") != null)
422         {
423             if (defaultValues.isManyToMany())
424             {
425                 throw new DeploymentException("Foreign key mapping-style "
426                         + "is not allowed for many-to-many relationsips.");
427             }
428             return FOREIGN_KEY;
429         }
430
431         // no mapping style element, will use defaultValues
432
return defaultValues.mappingStyle;
433     }
434
435     private static Element JavaDoc getMappingElement(Element JavaDoc element) throws DeploymentException
436     {
437
438         // if defaults check for preferred-relation-mapping
439
if ("defaults".equals(element.getTagName()))
440         {
441             return element;
442         }
443
444         // check for table mapping style
445
Element JavaDoc tableMappingElement = MetaData.getOptionalChild(element, "relation-table-mapping");
446         if (tableMappingElement != null)
447         {
448             return tableMappingElement;
449         }
450
451         // check for foreign-key mapping style
452
Element JavaDoc foreignKeyMappingElement = MetaData.getOptionalChild(element, "foreign-key-mapping");
453         if (foreignKeyMappingElement != null)
454         {
455             return foreignKeyMappingElement;
456         }
457         return null;
458     }
459
460     private static Element JavaDoc getEJBRelationshipRoleElement(Element JavaDoc element, JDBCRelationshipRoleMetaData defaultRole)
461             throws DeploymentException
462     {
463
464         String JavaDoc roleName = defaultRole.getRelationshipRoleName();
465
466         if (roleName == null)
467             throw new DeploymentException("No ejb-relationship-role-name element found");
468
469         Iterator JavaDoc iter = MetaData.getChildrenByTagName(element, "ejb-relationship-role");
470         if (!iter.hasNext())
471         {
472             throw new DeploymentException("No ejb-relationship-role " + "elements found");
473         }
474
475         Element JavaDoc roleElement = null;
476         for (int i = 0; iter.hasNext(); i++)
477         {
478             // only 2 roles are allowed
479
if (i > 1)
480             {
481                 throw new DeploymentException("Expected only 2 " + "ejb-relationship-role but found more then 2");
482             }
483
484             Element JavaDoc tempElement = (Element JavaDoc) iter.next();
485             if (roleName.equals(MetaData.getUniqueChildContent(tempElement, "ejb-relationship-role-name")))
486             {
487                 roleElement = tempElement;
488             }
489         }
490
491         if (roleElement == null)
492         {
493             throw new DeploymentException("An ejb-relationship-role element was " + "not found for role '" + roleName
494                     + "'");
495         }
496         return roleElement;
497     }
498
499     /**
500      * Gets the relation name. Relation name is loaded from the
501      * ejb-relation-name element.
502      *
503      * @return the name of this relation
504      */

505     public String JavaDoc getRelationName()
506     {
507         return relationName;
508     }
509
510     /**
511      * Gets the left jdbc relationship role. The relationship role is loaded
512      * from an ejb-relationship-role. Left/right assignment is completely
513      * arbitrary.
514      *
515      * @return the left JDBCRelationshipRoleMetaData
516      */

517     public JDBCRelationshipRoleMetaData getLeftRelationshipRole()
518     {
519         return left;
520     }
521
522     /**
523      * Gets the right jdbc relationship role. The relationship role is loaded
524      * from an ejb-relationship-role. Left/right assignment is completely
525      * arbitrary.
526      *
527      * @return the right JDBCRelationshipRoleMetaData
528      */

529     public JDBCRelationshipRoleMetaData getRightRelationshipRole()
530     {
531         return right;
532     }
533
534     /**
535      * Gets the relationship role related to the specified role.
536      *
537      * @param role the relationship role that the related role is desired
538      * @return the relationship role related to the specified role. right role
539      * of this relation
540      */

541     public JDBCRelationshipRoleMetaData getOtherRelationshipRole(JDBCRelationshipRoleMetaData role)
542     {
543
544         if (left == role)
545         {
546             return right;
547         }
548         else if (right == role)
549         {
550             return left;
551         }
552         else
553         {
554             throw new IllegalArgumentException JavaDoc("Specified role is not the left " + "or right role. role=" + role);
555         }
556     }
557
558     /**
559      * Should this relation be mapped to a relation table.
560      *
561      * @return true if this relation is mapped to a table
562      */

563     public boolean isTableMappingStyle()
564     {
565         return mappingStyle == TABLE;
566     }
567
568     /**
569      * Should this relation use foreign keys for storage.
570      *
571      * @return true if this relation is mapped to foreign keys
572      */

573     public boolean isForeignKeyMappingStyle()
574     {
575         return mappingStyle == FOREIGN_KEY;
576     }
577
578     /**
579      * Gets the name of the datasource in jndi for this entity
580      *
581      * @return the name of datasource in jndi
582      */

583     private String JavaDoc getDataSourceName()
584     {
585         return dataSourceName;
586     }
587
588     /**
589      * Gets the jdbc type mapping for this entity
590      *
591      * @return the jdbc type mapping for this entity
592      */

593     public JDBCTypeMappingMetaData getTypeMapping() throws DeploymentException
594    {
595       if(datasourceMapping == null)
596       {
597          throw new DeploymentException("type-mapping is not initialized: " + dataSourceName
598             + " was not deployed or type-mapping was not configured.");
599       }
600
601         return datasourceMapping;
602     }
603
604     /**
605      * Gets the name of the relation table.
606      *
607      * @return the name of the relation table to which is relation is mapped
608      */

609     public String JavaDoc getDefaultTableName()
610     {
611         return tableName;
612     }
613
614     /**
615      * Gets the (user-defined) SQL commands that should be issued to the db
616      * after table creation.
617      *
618      * @return the SQL command
619      */

620     public ArrayList JavaDoc getDefaultTablePostCreateCmd()
621     {
622         return tablePostCreateCmd;
623     }
624
625     /**
626      * Does the table exist yet? This does not mean that table has been created
627      * by the appilcation, or the the database metadata has been checked for the
628      * existance of the table, but that at this point the table is assumed to
629      * exist.
630      *
631      * @return true if the table exists
632      */

633     public boolean isTableCreated()
634     {
635         return tableCreated;
636     }
637
638     public void setTableCreated()
639     {
640         tableCreated = true;
641     }
642
643     /**
644      * Sets table dropped flag.
645      */

646     public void setTableDropped()
647     {
648         this.tableDropped = true;
649     }
650
651     public boolean isTableDropped()
652     {
653         return tableDropped;
654     }
655
656     /**
657      * Should the relation table be created on startup.
658      *
659      * @return true if the store mananager should attempt to create the relation
660      * table
661      */

662     public boolean getCreateTable()
663     {
664         return createTable;
665     }
666
667     /**
668      * Should the relation table be removed on shutdown.
669      *
670      * @return true if the store mananager should attempt to remove the relation
671      * table
672      */

673     public boolean getRemoveTable()
674     {
675         return removeTable;
676     }
677
678     /**
679      * Should the relation table be altered on deploy.
680      */

681     public boolean getAlterTable()
682     {
683         return alterTable;
684     }
685
686     /**
687      * When the relation table is created, should it have a primary key
688      * constraint.
689      *
690      * @return true if the store mananager should add a primary key constraint
691      * to the the create table sql statement
692      */

693     public boolean hasPrimaryKeyConstraint()
694     {
695         return primaryKeyConstraint;
696     }
697
698     /**
699      * Is this relation read-only?
700      */

701     public boolean isReadOnly()
702     {
703         return readOnly;
704     }
705
706     /**
707      * Gets the read time out length.
708      */

709     public int getReadTimeOut()
710     {
711         return readTimeOut;
712     }
713
714     /**
715      * Should select queries do row locking
716      */

717     public boolean hasRowLocking()
718     {
719         return rowLocking;
720     }
721
722     private String JavaDoc createDefaultTableName()
723     {
724         String JavaDoc defaultTableName = left.getEntity().getName();
725         if (left.getCMRFieldName() != null)
726         {
727             defaultTableName += "_" + left.getCMRFieldName();
728         }
729         defaultTableName += "_" + right.getEntity().getName();
730         if (right.getCMRFieldName() != null)
731         {
732             defaultTableName += "_" + right.getCMRFieldName();
733         }
734         return defaultTableName;
735     }
736
737     private boolean isManyToMany()
738     {
739         return left.isMultiplicityMany() && right.isMultiplicityMany();
740     }
741
742     public synchronized DataSource JavaDoc getDataSource()
743     {
744         if (dataSource == null)
745         {
746             try
747             {
748                 InitialContext JavaDoc context = new InitialContext JavaDoc();
749                 dataSource = (DataSource JavaDoc) context.lookup(dataSourceName);
750             }
751             catch (NamingException JavaDoc e)
752             {
753                 throw new EJBException JavaDoc("Data source for relationship named " + relationName + " not found "
754                         + dataSourceName);
755             }
756         }
757         return dataSource;
758     }
759 }
760
Popular Tags