001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.amber.cfg;
031:
032: import com.caucho.amber.AmberTableCache;
033: import com.caucho.amber.field.AmberField;
034: import com.caucho.amber.field.Id;
035: import com.caucho.amber.field.IdField;
036: import com.caucho.amber.field.KeyPropertyField;
037: import com.caucho.amber.field.PropertyField;
038: import com.caucho.amber.manager.AmberPersistenceUnit;
039: import com.caucho.amber.table.Column;
040: import com.caucho.amber.table.Table;
041: import com.caucho.amber.type.*;
042: import com.caucho.bytecode.JAnnotation;
043: import com.caucho.bytecode.JClass;
044: import com.caucho.bytecode.JMethod;
045: import com.caucho.config.ConfigException;
046: import com.caucho.config.types.Period;
047: import com.caucho.util.L10N;
048:
049: import javax.persistence.AttributeOverrides;
050: import javax.persistence.InheritanceType;
051: import javax.persistence.MappedSuperclass;
052: import java.sql.SQLException;
053: import java.util.ArrayList;
054: import java.util.HashMap;
055: import java.util.logging.Logger;
056:
057: /**
058: * Configuration for an entity bean
059: */
060: public class EntityIntrospector extends BaseConfigIntrospector {
061: private static final L10N L = new L10N(EntityIntrospector.class);
062: private static final Logger log = Logger
063: .getLogger(EntityIntrospector.class.getName());
064:
065: // EntityType or MappedSuperclassType.
066: HashMap<String, RelatedType> _relatedTypeMap = new HashMap<String, RelatedType>();
067:
068: /**
069: * Creates the introspector.
070: */
071: public EntityIntrospector(AmberPersistenceUnit persistenceUnit) {
072: super (persistenceUnit);
073: }
074:
075: /**
076: * Returns true for entity type.
077: */
078: public boolean isEntity(JClass type) {
079: getInternalEntityConfig(type, _annotationCfg);
080: JAnnotation entityAnn = _annotationCfg.getAnnotation();
081: EntityConfig entityConfig = _annotationCfg.getEntityConfig();
082:
083: return (!_annotationCfg.isNull());
084: }
085:
086: /**
087: * Introspects.
088: */
089: public RelatedType introspect(JClass type) throws ConfigException,
090: SQLException {
091: RelatedType entityType = null;
092:
093: try {
094: getInternalEntityConfig(type, _annotationCfg);
095:
096: JAnnotation entityAnn = _annotationCfg.getAnnotation();
097: EntityConfig entityConfig = _annotationCfg
098: .getEntityConfig();
099:
100: boolean isEntity = !_annotationCfg.isNull();
101:
102: boolean isMappedSuperclass = false;
103: JAnnotation mappedSuperAnn = null;
104: MappedSuperclassConfig mappedSuperConfig = null;
105:
106: String typeName;
107:
108: MappedSuperclassConfig mappedSuperOrEntityConfig = null;
109:
110: if (isEntity) {
111: mappedSuperOrEntityConfig = entityConfig;
112:
113: if (entityConfig != null)
114: typeName = entityConfig.getClassName();
115: else
116: typeName = entityAnn.getString("name");
117: } else {
118: getInternalMappedSuperclassConfig(type, _annotationCfg);
119: mappedSuperAnn = _annotationCfg.getAnnotation();
120: mappedSuperConfig = _annotationCfg
121: .getMappedSuperclassConfig();
122:
123: isMappedSuperclass = !_annotationCfg.isNull();
124:
125: if (isMappedSuperclass) {
126: mappedSuperOrEntityConfig = mappedSuperConfig;
127:
128: if (mappedSuperConfig != null)
129: typeName = mappedSuperConfig.getClassName();
130: else
131: typeName = mappedSuperAnn.getString("name");
132: } else
133: throw new ConfigException(
134: L
135: .l(
136: "'{0}' is not an @Entity or @MappedSuperclass.",
137: type));
138: }
139:
140: // Validates the type
141: String entityName;
142: RelatedType parentType = null;
143: JAnnotation inheritanceAnn = null;
144: InheritanceConfig inheritanceConfig = null;
145: JClass rootClass = type;
146: JAnnotation rootEntityAnn = null;
147: EntityConfig rootEntityConfig = null;
148:
149: if (isEntity || isMappedSuperclass) {
150: validateType(type, isEntity);
151:
152: // Inheritance annotation/configuration is specified
153: // on the entity class that is the root of the entity
154: // class hierarachy.
155:
156: getInternalInheritanceConfig(type, _annotationCfg);
157: inheritanceAnn = _annotationCfg.getAnnotation();
158: inheritanceConfig = _annotationCfg
159: .getInheritanceConfig();
160:
161: boolean hasInheritance = !_annotationCfg.isNull();
162:
163: for (JClass parentClass = type.getSuperClass(); parentClass != null; parentClass = parentClass
164: .getSuperClass()) {
165:
166: getInternalEntityConfig(parentClass, _annotationCfg);
167:
168: if (_annotationCfg.isNull())
169: break;
170:
171: rootEntityAnn = _annotationCfg.getAnnotation();
172: rootEntityConfig = _annotationCfg.getEntityConfig();
173:
174: rootClass = parentClass;
175:
176: if (hasInheritance)
177: throw new ConfigException(
178: L
179: .l(
180: "'{0}' cannot have @Inheritance. It must be specified on the entity class that is the root of the entity class hierarchy.",
181: type));
182:
183: getInternalInheritanceConfig(rootClass,
184: _annotationCfg);
185: inheritanceAnn = _annotationCfg.getAnnotation();
186: inheritanceConfig = _annotationCfg
187: .getInheritanceConfig();
188:
189: hasInheritance = !_annotationCfg.isNull();
190: }
191:
192: // jpa/0ge2
193: // if (hasInheritance) {
194:
195: for (JClass parentClass = type.getSuperClass(); parentClass != null; parentClass = parentClass
196: .getSuperClass()) {
197:
198: getInternalEntityConfig(parentClass, _annotationCfg);
199:
200: if (!_annotationCfg.isNull()) {
201: parentType = introspect(parentClass);
202:
203: if (parentClass.isAbstract()
204: && !type.isAbstract()) {
205: // jpa/0gg0, jpa/0gg1
206: // XXX entityType.addAbstractEntityParentFields();
207: }
208:
209: break;
210: }
211:
212: // jpa/0ge2
213: getInternalMappedSuperclassConfig(parentClass,
214: _annotationCfg);
215:
216: if (!_annotationCfg.isNull()) {
217: parentType = introspect(parentClass);
218: break;
219: }
220: }
221:
222: if (isEntity) {
223: if (entityConfig == null)
224: entityName = entityAnn.getString("name");
225: else {
226: entityName = entityConfig.getClassName();
227:
228: int p = entityName.lastIndexOf('.');
229:
230: if (p > 0)
231: entityName = entityName.substring(p + 1);
232: }
233: } else { // jpa/0ge2
234: if (mappedSuperConfig == null)
235: entityName = mappedSuperAnn.getString("name");
236: else {
237: entityName = mappedSuperConfig.getClassName();
238:
239: int p = entityName.lastIndexOf('.');
240:
241: if (p > 0)
242: entityName = entityName.substring(p + 1);
243: }
244: }
245: } else {
246: entityName = type.getName();
247: }
248:
249: if ((entityName == null) || "".equals(entityName)) {
250: entityName = type.getName();
251: int p = entityName.lastIndexOf('.');
252: if (p > 0)
253: entityName = entityName.substring(p + 1);
254: }
255:
256: entityType = _relatedTypeMap.get(entityName);
257:
258: if (entityType != null)
259: return entityType;
260:
261: if (isEntity) {
262: entityType = _persistenceUnit.createEntity(entityName,
263: type);
264: } else {
265: entityType = _persistenceUnit.createMappedSuperclass(
266: entityName, type);
267: }
268:
269: _relatedTypeMap.put(entityName, entityType);
270:
271: // Adds entity listeners, if any.
272: introspectEntityListeners(type, entityType,
273: _persistenceUnit);
274:
275: // Adds sql result set mappings, if any.
276: introspectSqlResultSetMappings(type, entityType, typeName);
277:
278: // Adds named queries, if any.
279: introspectNamedQueries(type, typeName);
280: introspectNamedNativeQueries(type, typeName);
281:
282: boolean isField = isField(type, mappedSuperOrEntityConfig,
283: false);
284:
285: if (isField)
286: entityType.setFieldAccess(true);
287:
288: // jpa/0ge2
289: entityType.setInstanceClassName(type.getName()
290: + "__ResinExt");
291: entityType.setEnhanced(true);
292:
293: Table table = null;
294:
295: getInternalTableConfig(type, _annotationCfg);
296: JAnnotation tableAnn = _annotationCfg.getAnnotation();
297: TableConfig tableConfig = _annotationCfg.getTableConfig();
298:
299: String tableName = null;
300:
301: if (tableAnn != null)
302: tableName = (String) tableAnn.get("name");
303: else if (tableConfig != null)
304: tableName = tableConfig.getName();
305:
306: // jpa/0gg0, jpa/0gg2
307: if (isEntity) { // && ! type.isAbstract()) {
308:
309: InheritanceType strategy = null;
310:
311: if (inheritanceAnn != null)
312: strategy = (InheritanceType) inheritanceAnn
313: .get("strategy");
314: else if (inheritanceConfig != null)
315: strategy = inheritanceConfig.getStrategy();
316:
317: boolean hasTableConfig = true;
318:
319: if (tableName == null || tableName.equals("")) {
320: hasTableConfig = false;
321: tableName = toSqlName(entityName);
322: }
323:
324: // jpa/0gg2
325: if (type.isAbstract()
326: && strategy != InheritanceType.JOINED
327: && !hasTableConfig) {
328: // jpa/0gg0
329: } else if ((parentType == null)
330: || (parentType instanceof MappedSuperclassType)) {
331:
332: entityType.setTable(_persistenceUnit
333: .createTable(tableName));
334: } else if (strategy == InheritanceType.JOINED) {
335: entityType.setTable(_persistenceUnit
336: .createTable(tableName));
337:
338: getInternalTableConfig(rootClass, _annotationCfg);
339: JAnnotation rootTableAnn = _annotationCfg
340: .getAnnotation();
341: TableConfig rootTableConfig = _annotationCfg
342: .getTableConfig();
343:
344: String rootTableName = null;
345:
346: if (rootTableAnn != null)
347: rootTableName = (String) rootTableAnn
348: .get("name");
349: else if (rootTableConfig != null)
350: rootTableName = rootTableConfig.getName();
351:
352: if (rootTableName == null
353: || rootTableName.equals("")) {
354:
355: String rootEntityName;
356:
357: if (rootEntityAnn != null)
358: rootEntityName = rootEntityAnn
359: .getString("name");
360: else {
361: rootEntityName = rootEntityConfig
362: .getClassName();
363:
364: int p = rootEntityName.lastIndexOf('.');
365:
366: if (p > 0)
367: rootEntityName = rootEntityName
368: .substring(p + 1);
369: }
370:
371: if (rootEntityName.equals("")) {
372: rootEntityName = rootClass.getName();
373:
374: int p = rootEntityName.lastIndexOf('.');
375:
376: if (p > 0)
377: rootEntityName = rootEntityName
378: .substring(p + 1);
379: }
380:
381: rootTableName = toSqlName(rootEntityName);
382: }
383:
384: entityType.setRootTableName(rootTableName);
385: } else
386: entityType.setTable(parentType.getTable());
387: }
388:
389: JAnnotation tableCache = type
390: .getAnnotation(AmberTableCache.class);
391:
392: if (tableCache != null) {
393: entityType.getTable().setReadOnly(
394: tableCache.getBoolean("readOnly"));
395:
396: long cacheTimeout = Period.toPeriod(tableCache
397: .getString("timeout"));
398: entityType.getTable().setCacheTimeout(cacheTimeout);
399: }
400:
401: getInternalSecondaryTableConfig(type, _annotationCfg);
402: JAnnotation secondaryTableAnn = _annotationCfg
403: .getAnnotation();
404: SecondaryTableConfig secondaryTableConfig = _annotationCfg
405: .getSecondaryTableConfig();
406:
407: Table secondaryTable = null;
408:
409: if ((inheritanceAnn != null) || (inheritanceConfig != null))
410: introspectInheritance(_persistenceUnit, entityType,
411: type, inheritanceAnn, inheritanceConfig);
412:
413: if ((secondaryTableAnn != null)
414: || (secondaryTableConfig != null)) {
415: String secondaryName;
416:
417: if (secondaryTableAnn != null)
418: secondaryName = secondaryTableAnn.getString("name");
419: else
420: secondaryName = secondaryTableConfig.getName();
421:
422: secondaryTable = _persistenceUnit
423: .createTable(secondaryName);
424:
425: entityType.addSecondaryTable(secondaryTable);
426:
427: // XXX: pk
428: }
429:
430: getInternalIdClassConfig(type, _annotationCfg);
431: JAnnotation idClassAnn = _annotationCfg.getAnnotation();
432: IdClassConfig idClassConfig = _annotationCfg
433: .getIdClassConfig();
434:
435: JClass idClass = null;
436: if (!_annotationCfg.isNull()) {
437:
438: if (idClassAnn != null)
439: idClass = idClassAnn.getClass("value");
440: else {
441: String s = idClassConfig.getClassName();
442: idClass = _persistenceUnit.getJClassLoader()
443: .forName(s);
444: }
445:
446: // XXX: temp. introspects idClass as an embeddable type.
447: EmbeddableType embeddable = _persistenceUnit
448: .getEmbeddableIntrospector()
449: .introspect(idClass);
450:
451: // jpa/0i49 vs jpa/0i40
452: // embeddable.setFieldAccess(isField);
453: }
454:
455: if (entityType.getId() != null) {
456: } else if (isField)
457: introspectIdField(_persistenceUnit, entityType,
458: parentType, type, idClass,
459: mappedSuperOrEntityConfig);
460: else {
461: introspectIdMethod(_persistenceUnit, entityType,
462: parentType, type, idClass,
463: mappedSuperOrEntityConfig);
464: }
465:
466: HashMap<String, IdConfig> idMap = null;
467:
468: AttributesConfig attributes = null;
469:
470: if (mappedSuperOrEntityConfig != null) {
471: attributes = mappedSuperOrEntityConfig.getAttributes();
472:
473: if (attributes != null)
474: idMap = attributes.getIdMap();
475: }
476:
477: // if ((idMap == null) || (idMap.size() == 0)) {
478: // idMap = entityType.getSuperClass();
479: // }
480:
481: if (isEntity && (entityType.getId() == null)
482: && ((idMap == null) || (idMap.size() == 0)))
483: throw new ConfigException(
484: L
485: .l(
486: "{0} does not have any primary keys. Entities must have at least one @Id or exactly one @EmbeddedId field.",
487: entityType.getName()));
488:
489: // Introspect overridden attributes. (jpa/0ge2)
490: introspectAttributeOverrides(entityType, type);
491:
492: if (isField)
493: introspectFields(_persistenceUnit, entityType,
494: parentType, type, mappedSuperOrEntityConfig,
495: false);
496: else
497: introspectMethods(_persistenceUnit, entityType,
498: parentType, type, mappedSuperOrEntityConfig);
499:
500: if (isEntity) {
501: introspectCallbacks(type, entityType);
502:
503: if (secondaryTableAnn != null) {
504: Object[] join = (Object[]) secondaryTableAnn
505: .get("pkJoinColumns");
506:
507: JAnnotation[] joinAnn = null;
508:
509: if (join != null) {
510: joinAnn = new JAnnotation[join.length];
511: System.arraycopy(join, 0, joinAnn, 0,
512: join.length);
513: }
514:
515: linkSecondaryTable(entityType.getTable(),
516: secondaryTable, joinAnn);
517: }
518: }
519: } catch (ConfigException e) {
520: if (entityType != null)
521: entityType.setConfigException(e);
522:
523: throw e;
524: } catch (SQLException e) {
525: if (entityType != null)
526: entityType.setConfigException(e);
527:
528: throw e;
529: } catch (RuntimeException e) {
530: if (entityType != null)
531: entityType.setConfigException(e);
532:
533: throw e;
534: }
535:
536: return entityType;
537: }
538:
539: private void introspectAttributeOverrides(RelatedType relatedType,
540: JClass type) {
541: RelatedType parent = relatedType.getParentType();
542:
543: if (parent == null)
544: return;
545:
546: boolean isAbstract = parent.getBeanClass().isAbstract();
547:
548: if (parent instanceof EntityType && !isAbstract)
549: return;
550:
551: _depCompletions.add(new AttributeOverrideCompletion(
552: relatedType, type));
553: }
554: }
|