001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc.metadata;
023:
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.Map;
031:
032: import org.w3c.dom.Element;
033:
034: import org.jboss.deployment.DeploymentException;
035: import org.jboss.metadata.ApplicationMetaData;
036: import org.jboss.metadata.BeanMetaData;
037: import org.jboss.metadata.EntityMetaData;
038: import org.jboss.metadata.MetaData;
039: import org.jboss.metadata.RelationMetaData;
040:
041: import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
042:
043: /**
044: * This immutable class contains information about the application
045: *
046: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
047: * @author <a href="sebastien.alborini@m4x.org">Sebastien Alborini</a>
048: * @author <a href="alex@jboss.org">Alexey Loubyansky</a>
049: * @version $Revision: 57209 $
050: */
051: public final class JDBCApplicationMetaData {
052: private final static Class JDBC_PM = org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.class;
053:
054: /**
055: * The class loader for this application. The class loader is used to
056: * load all classes used by this application.
057: */
058: private final ClassLoader classLoader;
059:
060: /**
061: * Application metadata loaded from the ejb-jar.xml file
062: */
063: private final ApplicationMetaData applicationMetaData;
064:
065: /**
066: * Map with user defined type mapping, e.g. enum mappings
067: */
068: private final Map userTypeMappings;
069:
070: /**
071: * Map of the type mappings by name.
072: */
073: private final Map typeMappings = new HashMap();
074:
075: /**
076: * Map of the entities managed by jbosscmp-jdbc by bean name.
077: */
078: private final Map entities = new HashMap();
079:
080: /**
081: * Collection of relations in this application.
082: */
083: private final Collection relationships = new HashSet();
084:
085: /**
086: * Map of the collection relationship roles for each entity by entity object.
087: */
088: private final Map entityRoles = new HashMap();
089:
090: /**
091: * Map of the dependent value classes by java class type.
092: */
093: private final Map valueClasses = new HashMap();
094:
095: /**
096: * Map from abstract schema name to entity name
097: */
098: private final Map entitiesByAbstractSchemaName = new HashMap();
099:
100: /**
101: * Map from entity interface(s) java type to entity name
102: */
103: private final Map entitiesByInterface = new HashMap();
104:
105: /**
106: * Map of the entity commands by name.
107: */
108: private final Map entityCommands = new HashMap();
109:
110: /**
111: * Constructs jdbc application meta data with the data from the
112: * applicationMetaData.
113: *
114: * @param applicationMetaData the application data loaded from
115: * the ejb-jar.xml file
116: * @param classLoader the ClassLoader used to load the classes
117: * of the application
118: * @throws DeploymentException if an problem occures while loading
119: * the classes or if data in the ejb-jar.xml is inconsistent
120: * with data from jbosscmp-jdbc.xml file
121: */
122: public JDBCApplicationMetaData(
123: ApplicationMetaData applicationMetaData,
124: ClassLoader classLoader) throws DeploymentException {
125: // the classloader is the same for all the beans in the application
126: this .classLoader = classLoader;
127: this .applicationMetaData = applicationMetaData;
128:
129: // create metadata for all jbosscmp-jdbc-managed cmp entities
130: // we do that here in case there is no jbosscmp-jdbc.xml
131: Iterator beans = applicationMetaData.getEnterpriseBeans();
132: while (beans.hasNext()) {
133: BeanMetaData bean = (BeanMetaData) beans.next();
134:
135: // only take entities
136: if (bean.isEntity()) {
137: EntityMetaData entity = (EntityMetaData) bean;
138:
139: // only take jbosscmp-jdbc-managed CMP entities
140: Class pm;
141: try {
142: pm = classLoader.loadClass(entity
143: .getContainerConfiguration()
144: .getPersistenceManager());
145: } catch (ClassNotFoundException e) {
146: throw new DeploymentException(
147: "Unable to load persistence manager", e);
148: }
149: if (entity.isCMP()
150: && (JDBC_PM.isAssignableFrom(pm) || pm
151: .getName()
152: .equals(
153: "org.jboss.ejb.plugins.cmp.jdbc2.JDBCStoreManager2"))) {
154: JDBCEntityMetaData jdbcEntity = new JDBCEntityMetaData(
155: this , entity);
156:
157: entities.put(entity.getEjbName(), jdbcEntity);
158:
159: String schemaName = jdbcEntity
160: .getAbstractSchemaName();
161: if (schemaName != null) {
162: entitiesByAbstractSchemaName.put(schemaName,
163: jdbcEntity);
164: }
165:
166: Class remote = jdbcEntity.getRemoteClass();
167: if (remote != null) {
168: entitiesByInterface.put(remote, jdbcEntity);
169: }
170:
171: Class local = jdbcEntity.getLocalClass();
172: if (local != null) {
173: entitiesByInterface.put(local, jdbcEntity);
174: }
175:
176: // initialized the entity roles collection
177: entityRoles.put(entity.getEjbName(), new HashSet());
178: }
179: }
180: }
181:
182: // relationships
183: Iterator iterator = applicationMetaData.getRelationships();
184: while (iterator.hasNext()) {
185: RelationMetaData relation = (RelationMetaData) iterator
186: .next();
187:
188: // Relationship metadata
189: JDBCRelationMetaData jdbcRelation = new JDBCRelationMetaData(
190: this , relation);
191: relationships.add(jdbcRelation);
192:
193: // Left relationship-role metadata
194: JDBCRelationshipRoleMetaData left = jdbcRelation
195: .getLeftRelationshipRole();
196: Collection leftEntityRoles = (Collection) entityRoles
197: .get(left.getEntity().getName());
198: leftEntityRoles.add(left);
199:
200: // Right relationship-role metadata
201: JDBCRelationshipRoleMetaData right = jdbcRelation
202: .getRightRelationshipRole();
203: Collection rightEntityRoles = (Collection) entityRoles
204: .get(right.getEntity().getName());
205: rightEntityRoles.add(right);
206: }
207:
208: userTypeMappings = Collections.EMPTY_MAP;
209: }
210:
211: /**
212: * Constructs application meta data with the data contained in the
213: * jboss-cmp xml element from a jbosscmp-jdbc xml file. Optional values
214: * of the xml element that are not present are loaded from the
215: * defalutValues parameter.
216: *
217: * @param element the xml Element which contains the metadata about
218: * this application
219: * @param defaultValues the JDBCApplicationMetaData which contains
220: * the values
221: * for optional elements of the element
222: * @throws DeploymentException if the xml element is not semantically correct
223: */
224: public JDBCApplicationMetaData(Element element,
225: JDBCApplicationMetaData defaultValues)
226: throws DeploymentException {
227: // importXml will be called at least once: with standardjbosscmp-jdbc.xml
228: // it may be called a second time with user-provided jbosscmp-jdbc.xml
229: // we must ensure to set all defaults values in the first call
230:
231: classLoader = defaultValues.classLoader;
232: applicationMetaData = defaultValues.applicationMetaData;
233:
234: Element userTypeMaps = MetaData.getOptionalChild(element,
235: "user-type-mappings");
236: if (userTypeMaps != null) {
237: userTypeMappings = new HashMap();
238: Iterator iter = MetaData.getChildrenByTagName(userTypeMaps,
239: "user-type-mapping");
240: while (iter.hasNext()) {
241: Element userTypeMappingEl = (Element) iter.next();
242: JDBCUserTypeMappingMetaData userTypeMapping = new JDBCUserTypeMappingMetaData(
243: userTypeMappingEl);
244: userTypeMappings.put(userTypeMapping.getJavaType(),
245: userTypeMapping);
246: }
247: } else
248: userTypeMappings = defaultValues.getUserTypeMappings();
249:
250: // type-mappings: (optional, always set in standardjbosscmp-jdbc.xml)
251: typeMappings.putAll(defaultValues.typeMappings);
252: Element typeMaps = MetaData.getOptionalChild(element,
253: "type-mappings");
254: if (typeMaps != null) {
255: for (Iterator i = MetaData.getChildrenByTagName(typeMaps,
256: "type-mapping"); i.hasNext();) {
257: Element typeMappingElement = (Element) i.next();
258: JDBCTypeMappingMetaData typeMapping = new JDBCTypeMappingMetaData(
259: typeMappingElement);
260: typeMappings.put(typeMapping.getName(), typeMapping);
261: }
262: }
263:
264: // dependent-value-objects
265: valueClasses.putAll(defaultValues.valueClasses);
266: Element valueClassesElement = MetaData.getOptionalChild(
267: element, "dependent-value-classes");
268: if (valueClassesElement != null) {
269: for (Iterator i = MetaData.getChildrenByTagName(
270: valueClassesElement, "dependent-value-class"); i
271: .hasNext();) {
272:
273: Element valueClassElement = (Element) i.next();
274: JDBCValueClassMetaData valueClass = new JDBCValueClassMetaData(
275: valueClassElement, classLoader);
276: valueClasses.put(valueClass.getJavaType(), valueClass);
277: }
278: }
279:
280: // entity-commands: (optional, always set in standardjbosscmp-jdbc.xml)
281: entityCommands.putAll(defaultValues.entityCommands);
282: Element entityCommandMaps = MetaData.getOptionalChild(element,
283: "entity-commands");
284: if (entityCommandMaps != null) {
285: for (Iterator i = MetaData.getChildrenByTagName(
286: entityCommandMaps, "entity-command"); i.hasNext();) {
287:
288: Element entityCommandElement = (Element) i.next();
289: JDBCEntityCommandMetaData entityCommand = new JDBCEntityCommandMetaData(
290: entityCommandElement);
291: entityCommands.put(entityCommand.getCommandName(),
292: entityCommand);
293: }
294: }
295:
296: // reserved words: (optional, always set in standardjbosscmp-jdbc.xml)
297: // list of reserved words that should be escaped in table names
298: Element rWords = MetaData.getOptionalChild(element,
299: "reserved-words");
300: if (rWords != null) {
301: for (Iterator i = MetaData.getChildrenByTagName(rWords,
302: "word"); i.hasNext();) {
303: Element rWord = (Element) i.next();
304: SQLUtil.addToRwords(MetaData.getElementContent(rWord));
305: }
306: }
307:
308: // defaults: apply defaults for entities (optional, always
309: // set in standardjbosscmp-jdbc.xml)
310: entities.putAll(defaultValues.entities);
311: entitiesByAbstractSchemaName
312: .putAll(defaultValues.entitiesByAbstractSchemaName);
313: entitiesByInterface.putAll(defaultValues.entitiesByInterface);
314: Element defaults = MetaData.getOptionalChild(element,
315: "defaults");
316: if (defaults != null) {
317: ArrayList values = new ArrayList(entities.values());
318: for (Iterator i = values.iterator(); i.hasNext();) {
319: JDBCEntityMetaData entityMetaData = (JDBCEntityMetaData) i
320: .next();
321:
322: // create the new metadata with the defaults applied
323: entityMetaData = new JDBCEntityMetaData(this , defaults,
324: entityMetaData);
325:
326: // replace the old meta data with the new
327: entities.put(entityMetaData.getName(), entityMetaData);
328:
329: String schemaName = entityMetaData
330: .getAbstractSchemaName();
331: if (schemaName != null) {
332: entitiesByAbstractSchemaName.put(schemaName,
333: entityMetaData);
334: }
335:
336: Class remote = entityMetaData.getRemoteClass();
337: if (remote != null) {
338: entitiesByInterface.put(remote, entityMetaData);
339: }
340:
341: Class local = entityMetaData.getLocalClass();
342: if (local != null) {
343: entitiesByInterface.put(local, entityMetaData);
344: }
345: }
346: }
347:
348: // enterprise-beans: apply entity specific configuration
349: // (only in jbosscmp-jdbc.xml)
350: Element enterpriseBeans = MetaData.getOptionalChild(element,
351: "enterprise-beans");
352: if (enterpriseBeans != null) {
353: for (Iterator i = MetaData.getChildrenByTagName(
354: enterpriseBeans, "entity"); i.hasNext();) {
355:
356: Element beanElement = (Element) i.next();
357:
358: // get entity by name, if not found, it is a config error
359: String ejbName = MetaData.getUniqueChildContent(
360: beanElement, "ejb-name");
361: JDBCEntityMetaData entityMetaData = getBeanByEjbName(ejbName);
362:
363: if (entityMetaData == null) {
364: throw new DeploymentException(
365: "Configuration found in "
366: + "jbosscmp-jdbc.xml for entity "
367: + ejbName
368: + " but bean "
369: + "is not a jbosscmp-jdbc-managed cmp entity in "
370: + "ejb-jar.xml");
371: }
372: entityMetaData = new JDBCEntityMetaData(this ,
373: beanElement, entityMetaData);
374: entities.put(entityMetaData.getName(), entityMetaData);
375:
376: String schemaName = entityMetaData
377: .getAbstractSchemaName();
378: if (schemaName != null) {
379: entitiesByAbstractSchemaName.put(schemaName,
380: entityMetaData);
381: }
382:
383: Class remote = entityMetaData.getRemoteClass();
384: if (remote != null) {
385: entitiesByInterface.put(remote, entityMetaData);
386: }
387:
388: Class local = entityMetaData.getLocalClass();
389: if (local != null) {
390: entitiesByInterface.put(local, entityMetaData);
391: }
392: }
393: }
394:
395: // defaults: apply defaults for relationships (optional, always
396: // set in standardjbosscmp-jdbc.xml)
397: if (defaults == null) {
398: // no defaults just copy over the existing relationships and roles
399: relationships.addAll(defaultValues.relationships);
400: entityRoles.putAll(defaultValues.entityRoles);
401: } else {
402:
403: // create a new empty role collection for each entity
404: for (Iterator i = entities.values().iterator(); i.hasNext();) {
405: JDBCEntityMetaData entity = (JDBCEntityMetaData) i
406: .next();
407: entityRoles.put(entity.getName(), new HashSet());
408: }
409:
410: // for each relationship, apply defaults and store
411: for (Iterator i = defaultValues.relationships.iterator(); i
412: .hasNext();) {
413:
414: JDBCRelationMetaData relationMetaData = (JDBCRelationMetaData) i
415: .next();
416:
417: // create the new metadata with the defaults applied
418: relationMetaData = new JDBCRelationMetaData(this ,
419: defaults, relationMetaData);
420:
421: // replace the old metadata with the new
422: relationships.add(relationMetaData);
423:
424: // store new left role
425: JDBCRelationshipRoleMetaData left = relationMetaData
426: .getLeftRelationshipRole();
427: Collection leftEntityRoles = (Collection) entityRoles
428: .get(left.getEntity().getName());
429: leftEntityRoles.add(left);
430:
431: // store new right role
432: JDBCRelationshipRoleMetaData right = relationMetaData
433: .getRightRelationshipRole();
434: Collection rightEntityRoles = (Collection) entityRoles
435: .get(right.getEntity().getName());
436: rightEntityRoles.add(right);
437: }
438: }
439:
440: // relationships: apply entity specific configuration
441: // (only in jbosscmp-jdbc.xml)
442: Element relationshipsElement = MetaData.getOptionalChild(
443: element, "relationships");
444: if (relationshipsElement != null) {
445:
446: // create a map of the relations by name (if it has a name)
447: Map relationByName = new HashMap();
448: for (Iterator i = relationships.iterator(); i.hasNext();) {
449: JDBCRelationMetaData relation = (JDBCRelationMetaData) i
450: .next();
451: if (relation.getRelationName() != null) {
452: relationByName.put(relation.getRelationName(),
453: relation);
454: }
455: }
456:
457: for (Iterator i = MetaData.getChildrenByTagName(
458: relationshipsElement, "ejb-relation"); i.hasNext();) {
459: Element relationElement = (Element) i.next();
460:
461: // get relation by name, if not found, it is a config error
462: String relationName = MetaData.getUniqueChildContent(
463: relationElement, "ejb-relation-name");
464: JDBCRelationMetaData oldRelation = (JDBCRelationMetaData) relationByName
465: .get(relationName);
466:
467: if (oldRelation == null) {
468: throw new DeploymentException(
469: "Configuration found in "
470: + "jbosscmp-jdbc.xml for relation "
471: + relationName
472: + " but relation is not a jbosscmp-jdbc-managed relation "
473: + "in ejb-jar.xml");
474: }
475: // create new metadata with relation specific config applied
476: JDBCRelationMetaData newRelation = new JDBCRelationMetaData(
477: this , relationElement, oldRelation);
478:
479: // replace the old metadata with the new
480: relationships.remove(oldRelation);
481: relationships.add(newRelation);
482:
483: // replace the old left role with the new
484: JDBCRelationshipRoleMetaData newLeft = newRelation
485: .getLeftRelationshipRole();
486: Collection leftEntityRoles = (Collection) entityRoles
487: .get(newLeft.getEntity().getName());
488: leftEntityRoles.remove(oldRelation
489: .getLeftRelationshipRole());
490: leftEntityRoles.add(newLeft);
491:
492: // replace the old right role with the new
493: JDBCRelationshipRoleMetaData newRight = newRelation
494: .getRightRelationshipRole();
495: Collection rightEntityRoles = (Collection) entityRoles
496: .get(newRight.getEntity().getName());
497: rightEntityRoles.remove(oldRelation
498: .getRightRelationshipRole());
499: rightEntityRoles.add(newRight);
500: }
501: }
502:
503: }
504:
505: /**
506: * Gets the type mapping with the specified name
507: * @param name the name for the type mapping
508: * @return the matching type mapping or null if not found
509: */
510: public JDBCTypeMappingMetaData getTypeMappingByName(String name) {
511: return (JDBCTypeMappingMetaData) typeMappings.get(name);
512: }
513:
514: /**
515: * Gets the relationship roles for the entity with the specified name.
516: * @param entityName the name of the entity whos roles are returned
517: * @return an unmodifiable collection of JDBCRelationshipRoles
518: * of the specified entity
519: */
520: public Collection getRolesForEntity(String entityName) {
521: Collection roles = (Collection) entityRoles.get(entityName);
522: return Collections.unmodifiableCollection(roles);
523: }
524:
525: /**
526: * Gets dependent value classes that are directly managed by the container.
527: * @returns an unmodifiable collection of JDBCValueClassMetaData
528: */
529: public Collection getValueClasses() {
530: return Collections
531: .unmodifiableCollection(valueClasses.values());
532: }
533:
534: /**
535: * Gets the classloader for this application which is used to load
536: * all classes.
537: * @return the ClassLoader for the application
538: */
539: public ClassLoader getClassLoader() {
540: return classLoader;
541: }
542:
543: /**
544: * Gets the metadata for an entity bean by name.
545: * @param name the name of the entity meta data to return
546: * @return the entity meta data for the specified name
547: */
548: public JDBCEntityMetaData getBeanByEjbName(String name) {
549: return (JDBCEntityMetaData) entities.get(name);
550: }
551:
552: /**
553: * Gets the entity command with the specified name
554: * @param name the name for the entity-command
555: * @return the matching entity command or null if not found
556: */
557: public JDBCEntityCommandMetaData getEntityCommandByName(String name) {
558: return (JDBCEntityCommandMetaData) entityCommands.get(name);
559: }
560:
561: public Map getUserTypeMappings() {
562: return Collections.unmodifiableMap(userTypeMappings);
563: }
564: }
|