PageRenderTime 28ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/addon-dbre/addon/src/main/java/org/springframework/roo/addon/dbre/addon/DbreDatabaseListenerImpl.java

http://github.com/SpringSource/spring-roo
Java | 918 lines | 654 code | 114 blank | 150 comment | 149 complexity | 3cfb6ea87f7ef0d0962aa59334ed7bb4 MD5 | raw file
  1. package org.springframework.roo.addon.dbre.addon;
  2. import static org.springframework.roo.addon.dbre.addon.model.DbreModelService.DBRE_XML;
  3. import static org.springframework.roo.model.JavaType.OBJECT;
  4. import static org.springframework.roo.model.RooJavaType.ROO_DB_MANAGED;
  5. import static org.springframework.roo.model.RooJavaType.ROO_IDENTIFIER;
  6. import static org.springframework.roo.model.RooJavaType.ROO_JAVA_BEAN;
  7. import static org.springframework.roo.model.RooJavaType.ROO_JPA_ENTITY;
  8. import static org.springframework.roo.model.RooJavaType.ROO_TO_STRING;
  9. import java.lang.reflect.Modifier;
  10. import java.util.ArrayList;
  11. import java.util.Collection;
  12. import java.util.HashSet;
  13. import java.util.LinkedHashMap;
  14. import java.util.LinkedHashSet;
  15. import java.util.List;
  16. import java.util.Map;
  17. import java.util.Set;
  18. import java.util.logging.Level;
  19. import org.apache.commons.lang3.StringUtils;
  20. import org.apache.commons.lang3.Validate;
  21. import org.apache.felix.scr.annotations.Component;
  22. import org.apache.felix.scr.annotations.Service;
  23. import org.osgi.framework.InvalidSyntaxException;
  24. import org.osgi.framework.ServiceReference;
  25. import org.springframework.roo.addon.dbre.addon.model.Column;
  26. import org.springframework.roo.addon.dbre.addon.model.Database;
  27. import org.springframework.roo.addon.dbre.addon.model.DbreModelService;
  28. import org.springframework.roo.addon.dbre.addon.model.Table;
  29. import org.springframework.roo.addon.jpa.addon.identifier.Identifier;
  30. import org.springframework.roo.addon.jpa.addon.identifier.IdentifierService;
  31. import org.springframework.roo.addon.layers.repository.jpa.addon.RepositoryJpaOperations;
  32. import org.springframework.roo.addon.layers.service.addon.ServiceOperations;
  33. import org.springframework.roo.addon.test.addon.integration.IntegrationTestOperations;
  34. import org.springframework.roo.classpath.PhysicalTypeCategory;
  35. import org.springframework.roo.classpath.PhysicalTypeIdentifier;
  36. import org.springframework.roo.classpath.PhysicalTypeMetadata;
  37. import org.springframework.roo.classpath.TypeLocationService;
  38. import org.springframework.roo.classpath.TypeManagementService;
  39. import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
  40. import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
  41. import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
  42. import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
  43. import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
  44. import org.springframework.roo.file.monitor.event.FileEvent;
  45. import org.springframework.roo.file.monitor.event.FileEventListener;
  46. import org.springframework.roo.file.monitor.event.FileOperation;
  47. import org.springframework.roo.metadata.AbstractHashCodeTrackingMetadataNotifier;
  48. import org.springframework.roo.metadata.MetadataItem;
  49. import org.springframework.roo.model.JavaPackage;
  50. import org.springframework.roo.model.JavaSymbolName;
  51. import org.springframework.roo.model.JavaType;
  52. import org.springframework.roo.model.JdkJavaType;
  53. import org.springframework.roo.process.manager.FileManager;
  54. import org.springframework.roo.project.Path;
  55. import org.springframework.roo.project.ProjectOperations;
  56. import org.springframework.roo.shell.Shell;
  57. import org.springframework.roo.support.util.CollectionUtils;
  58. /**
  59. * Implementation of {@link DbreDatabaseListener}.
  60. *
  61. * @author Alan Stewart
  62. * @author Juan Carlos GarcĂ­a
  63. * @since 1.1
  64. */
  65. @Component
  66. @Service
  67. public class DbreDatabaseListenerImpl extends AbstractHashCodeTrackingMetadataNotifier implements
  68. IdentifierService, FileEventListener {
  69. private static final JavaSymbolName DB_MANAGED = new JavaSymbolName("dbManaged");
  70. private static final String IDENTIFIER_TYPE = "identifierType";
  71. private static final String PRIMARY_KEY_SUFFIX = "PK";
  72. private static final String SEQUENCE_NAME_FIELD = "sequenceName";
  73. private static final String VERSION = "version";
  74. private static final String VERSION_FIELD = "versionField";
  75. private DbreModelService dbreModelService;
  76. private FileManager fileManager;
  77. private IntegrationTestOperations integrationTestOperations;
  78. private ProjectOperations projectOperations;
  79. private RepositoryJpaOperations repositoryJpaOperations;
  80. private ServiceOperations serviceOperations;
  81. private Shell shell;
  82. private TypeLocationService typeLocationService;
  83. private TypeManagementService typeManagementService;
  84. private Map<JavaType, List<Identifier>> identifierResults;
  85. private void createIdentifierClass(final JavaType identifierType) {
  86. final List<AnnotationMetadataBuilder> identifierAnnotations =
  87. new ArrayList<AnnotationMetadataBuilder>();
  88. final AnnotationMetadataBuilder identifierBuilder =
  89. new AnnotationMetadataBuilder(ROO_IDENTIFIER);
  90. identifierBuilder.addBooleanAttribute(DB_MANAGED.getSymbolName(), true);
  91. identifierAnnotations.add(identifierBuilder);
  92. // Produce identifier itself
  93. final String declaredByMetadataId =
  94. PhysicalTypeIdentifier.createIdentifier(identifierType, getProjectOperations()
  95. .getPathResolver().getFocusedPath(Path.SRC_MAIN_JAVA));
  96. final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
  97. new ClassOrInterfaceTypeDetailsBuilder(declaredByMetadataId, Modifier.PUBLIC
  98. | Modifier.FINAL, identifierType, PhysicalTypeCategory.CLASS);
  99. cidBuilder.setAnnotations(identifierAnnotations);
  100. getTypeManagementService().createOrUpdateTypeOnDisk(cidBuilder.build());
  101. getShell().flash(Level.FINE, "Created " + identifierType.getFullyQualifiedTypeName(),
  102. DbreDatabaseListenerImpl.class.getName());
  103. getShell().flash(Level.FINE, "", DbreDatabaseListenerImpl.class.getName());
  104. }
  105. /**
  106. * Creates a new DBRE-managed entity from the given table
  107. *
  108. * @param javaType the name of the entity to be created (required)
  109. * @param table the table from which to create the entity (required)
  110. * @return the newly created entity
  111. */
  112. private ClassOrInterfaceTypeDetails createNewManagedEntityFromTable(final JavaType javaType,
  113. final Table table) {
  114. // Create type annotations for new entity
  115. final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
  116. annotations.add(new AnnotationMetadataBuilder(ROO_JAVA_BEAN));
  117. annotations.add(new AnnotationMetadataBuilder(ROO_TO_STRING));
  118. // Find primary key from db metadata and add identifier attributes to
  119. // @RooJpaEntity
  120. final AnnotationMetadataBuilder jpaAnnotationBuilder =
  121. new AnnotationMetadataBuilder(ROO_JPA_ENTITY);
  122. manageIdentifier(javaType, jpaAnnotationBuilder, new HashSet<JavaSymbolName>(), table);
  123. if (!hasVersionField(table)) {
  124. jpaAnnotationBuilder.addStringAttribute(VERSION_FIELD, "");
  125. }
  126. if (table.isDisableGeneratedIdentifiers()) {
  127. jpaAnnotationBuilder.addStringAttribute(SEQUENCE_NAME_FIELD, "");
  128. }
  129. jpaAnnotationBuilder.addStringAttribute("table", table.getName());
  130. if (!DbreModelService.NO_SCHEMA_REQUIRED.equals(table.getSchema().getName())) {
  131. jpaAnnotationBuilder.addStringAttribute("schema", table.getSchema().getName());
  132. }
  133. annotations.add(jpaAnnotationBuilder);
  134. // Add @RooDbManaged
  135. annotations.add(getRooDbManagedAnnotation());
  136. final JavaType superclass = OBJECT;
  137. final List<JavaType> extendsTypes = new ArrayList<JavaType>();
  138. extendsTypes.add(superclass);
  139. // Create entity class
  140. final String declaredByMetadataId =
  141. PhysicalTypeIdentifier.createIdentifier(javaType, getProjectOperations().getPathResolver()
  142. .getFocusedPath(Path.SRC_MAIN_JAVA));
  143. final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
  144. new ClassOrInterfaceTypeDetailsBuilder(declaredByMetadataId, Modifier.PUBLIC, javaType,
  145. PhysicalTypeCategory.CLASS);
  146. cidBuilder.setExtendsTypes(extendsTypes);
  147. cidBuilder.setAnnotations(annotations);
  148. final ClassOrInterfaceTypeDetails entity = cidBuilder.build();
  149. getTypeManagementService().createOrUpdateTypeOnDisk(entity);
  150. getShell().flash(Level.FINE, "Created " + javaType.getFullyQualifiedTypeName(),
  151. DbreDatabaseListenerImpl.class.getName());
  152. getShell().flash(Level.FINE, "", DbreDatabaseListenerImpl.class.getName());
  153. return entity;
  154. }
  155. /**
  156. * Deletes the given {@link JavaType} for the given reason
  157. *
  158. * @param javaType the type to be deleted (required)
  159. * @param reason the reason for deletion (can be blank)
  160. */
  161. private void deleteJavaType(final JavaType javaType, final String reason) {
  162. final PhysicalTypeMetadata governorPhysicalTypeMetadata = getPhysicalTypeMetadata(javaType);
  163. if (governorPhysicalTypeMetadata != null) {
  164. final String filePath = governorPhysicalTypeMetadata.getPhysicalLocationCanonicalPath();
  165. if (getFileManager().exists(filePath)) {
  166. getFileManager().delete(filePath, reason);
  167. getShell().flash(Level.FINE, "Deleted " + javaType.getFullyQualifiedTypeName(),
  168. DbreDatabaseListenerImpl.class.getName());
  169. }
  170. getShell().flash(Level.FINE, "", DbreDatabaseListenerImpl.class.getName());
  171. }
  172. }
  173. private void deleteManagedType(final ClassOrInterfaceTypeDetails managedEntity,
  174. final String reason) {
  175. if (!isEntityDeletable(managedEntity)) {
  176. return;
  177. }
  178. deleteJavaType(managedEntity.getName(), reason);
  179. final JavaType identifierType = getIdentifierType(managedEntity.getName());
  180. for (final ClassOrInterfaceTypeDetails managedIdentifier : getManagedIdentifiers()) {
  181. if (managedIdentifier.getName().equals(identifierType)) {
  182. deleteJavaType(identifierType,
  183. "managed identifier of deleted type " + managedEntity.getName());
  184. break;
  185. }
  186. }
  187. }
  188. private void deserializeDatabase() {
  189. final Database database = getDbreModelService().getDatabase(true);
  190. if (database != null) {
  191. identifierResults = new LinkedHashMap<JavaType, List<Identifier>>();
  192. reverseEngineer(database);
  193. }
  194. }
  195. private JavaPackage getDestinationPackage(final Database database,
  196. final Set<ClassOrInterfaceTypeDetails> managedEntities) {
  197. JavaPackage destinationPackage = database.getDestinationPackage();
  198. if (destinationPackage == null) {
  199. if (!managedEntities.isEmpty() && !database.hasMultipleSchemas()) {
  200. // Take the package of the first one
  201. destinationPackage = managedEntities.iterator().next().getName().getPackage();
  202. }
  203. }
  204. // Fall back to project's top level package
  205. if (destinationPackage == null) {
  206. destinationPackage = getProjectOperations().getFocusedTopLevelPackage();
  207. }
  208. return destinationPackage;
  209. }
  210. public List<Identifier> getIdentifiers(final JavaType pkType) {
  211. if (identifierResults == null) {
  212. // Need to populate the identifier results before returning from
  213. // this method
  214. deserializeDatabase();
  215. }
  216. if (identifierResults == null) {
  217. // It's still null, so maybe the DBRE XML file isn't available at
  218. // this time or similar
  219. return null;
  220. }
  221. return identifierResults.get(pkType);
  222. }
  223. private List<Identifier> getIdentifiers(final Table table, final boolean usePrimaryKeys) {
  224. final List<Identifier> result = new ArrayList<Identifier>();
  225. // Add fields to the identifier class
  226. final Set<Column> columns = usePrimaryKeys ? table.getPrimaryKeys() : table.getColumns();
  227. for (final Column column : columns) {
  228. final String columnName = column.getName();
  229. JavaSymbolName fieldName;
  230. try {
  231. fieldName = new JavaSymbolName(DbreTypeUtils.suggestFieldName(columnName));
  232. } catch (final RuntimeException e) {
  233. throw new IllegalArgumentException("Failed to create field name for column '" + columnName
  234. + "' in table '" + table.getName() + "': " + e.getMessage());
  235. }
  236. final JavaType fieldType = column.getJavaType();
  237. final String columnDefinition =
  238. table.isIncludeNonPortableAttributes() ? column.getTypeName() : "";
  239. result.add(new Identifier(fieldName, fieldType, columnName, column.getColumnSize(), column
  240. .getScale(), columnDefinition));
  241. }
  242. return result;
  243. }
  244. private List<Identifier> getIdentifiersFromColumns(final Table table) {
  245. return getIdentifiers(table, false);
  246. }
  247. private List<Identifier> getIdentifiersFromPrimaryKeys(final Table table) {
  248. return getIdentifiers(table, true);
  249. }
  250. /**
  251. * Returns the type of ID that DBRE should use for the given entity
  252. *
  253. * @param entity the entity for which to get the ID type (required)
  254. * @return a non-<code>null</code> ID type
  255. */
  256. private JavaType getIdentifierType(final JavaType entity) {
  257. final PhysicalTypeMetadata governorPhysicalTypeMetadata = getPhysicalTypeMetadata(entity);
  258. if (governorPhysicalTypeMetadata != null) {
  259. final ClassOrInterfaceTypeDetails governorTypeDetails =
  260. governorPhysicalTypeMetadata.getMemberHoldingTypeDetails();
  261. final AnnotationMetadata jpaAnnotation = getJpaAnnotation(governorTypeDetails);
  262. if (jpaAnnotation != null) {
  263. final AnnotationAttributeValue<?> identifierTypeAttribute =
  264. jpaAnnotation.getAttribute(new JavaSymbolName(IDENTIFIER_TYPE));
  265. if (identifierTypeAttribute != null) {
  266. // The identifierType attribute exists, so get its value
  267. final JavaType identifierType = (JavaType) identifierTypeAttribute.getValue();
  268. if (identifierType != null && !JdkJavaType.isPartOfJavaLang(identifierType)) {
  269. return identifierType;
  270. }
  271. }
  272. }
  273. }
  274. // The JPA annotation's "identifierType" attribute does not exist or is
  275. // not a simple type, so return a default
  276. return new JavaType(entity.getFullyQualifiedTypeName() + PRIMARY_KEY_SUFFIX);
  277. }
  278. /**
  279. * Returns the JPA-related annotation on the given managed entity
  280. *
  281. * @param managedEntity an existing DBRE-managed entity (required)
  282. * @return <code>null</code> if there isn't one
  283. */
  284. private AnnotationMetadata getJpaAnnotation(final ClassOrInterfaceTypeDetails managedEntity) {
  285. // The @RooJpaEntity annotation takes precedence if present
  286. final AnnotationMetadata rooJpaEntity = managedEntity.getAnnotation(ROO_JPA_ENTITY);
  287. return rooJpaEntity;
  288. }
  289. private Set<ClassOrInterfaceTypeDetails> getManagedIdentifiers() {
  290. final Set<ClassOrInterfaceTypeDetails> managedIdentifierTypes =
  291. new LinkedHashSet<ClassOrInterfaceTypeDetails>();
  292. final Set<ClassOrInterfaceTypeDetails> identifierTypes =
  293. getTypeLocationService().findClassesOrInterfaceDetailsWithAnnotation(ROO_IDENTIFIER);
  294. for (final ClassOrInterfaceTypeDetails managedIdentifierType : identifierTypes) {
  295. final AnnotationMetadata identifierAnnotation =
  296. managedIdentifierType.getTypeAnnotation(ROO_IDENTIFIER);
  297. final AnnotationAttributeValue<?> attrValue = identifierAnnotation.getAttribute(DB_MANAGED);
  298. if (attrValue != null && (Boolean) attrValue.getValue()) {
  299. managedIdentifierTypes.add(managedIdentifierType);
  300. }
  301. }
  302. return managedIdentifierTypes;
  303. }
  304. private PhysicalTypeMetadata getPhysicalTypeMetadata(final JavaType javaType) {
  305. final String declaredByMetadataId =
  306. getTypeLocationService().getPhysicalTypeIdentifier(javaType);
  307. if (StringUtils.isBlank(declaredByMetadataId)) {
  308. return null;
  309. }
  310. return (PhysicalTypeMetadata) getMetadataService().get(declaredByMetadataId);
  311. }
  312. private AnnotationMetadataBuilder getRooDbManagedAnnotation() {
  313. final AnnotationMetadataBuilder rooDbManagedBuilder =
  314. new AnnotationMetadataBuilder(ROO_DB_MANAGED);
  315. rooDbManagedBuilder.addBooleanAttribute("automaticallyDelete", true);
  316. return rooDbManagedBuilder;
  317. }
  318. /**
  319. * Indicates whether the given entity has the standard annotations applied
  320. * by Roo, and no others.
  321. *
  322. * @param entity the entity to check (required)
  323. * @return <code>false</code> if any of the standard ones are missing or any
  324. * extra ones have been added
  325. */
  326. private boolean hasStandardEntityAnnotations(final ClassOrInterfaceTypeDetails entity) {
  327. final List<? extends AnnotationMetadata> typeAnnotations = entity.getAnnotations();
  328. // We expect four: RooDbManaged, RooJavaBean, RooToString, and either
  329. // RooEntity or RooJpaEntity
  330. if (typeAnnotations.size() != 4) {
  331. return false;
  332. }
  333. // There are exactly four - check for any non-standard ones
  334. for (final AnnotationMetadata annotation : typeAnnotations) {
  335. final JavaType annotationType = annotation.getAnnotationType();
  336. final boolean entityAnnotation = ROO_JPA_ENTITY.equals(annotationType);
  337. if (!entityAnnotation && !ROO_DB_MANAGED.equals(annotationType)
  338. && !ROO_JAVA_BEAN.equals(annotationType) && !ROO_TO_STRING.equals(annotationType)) {
  339. return false;
  340. }
  341. }
  342. return true;
  343. }
  344. private boolean hasVersionField(final Table table) {
  345. if (!table.isDisableVersionFields()) {
  346. for (final Column column : table.getColumns()) {
  347. if (VERSION.equalsIgnoreCase(column.getName())) {
  348. return true;
  349. }
  350. }
  351. }
  352. return false;
  353. }
  354. private boolean isEntityDeletable(final ClassOrInterfaceTypeDetails managedEntity) {
  355. final String declaredByMetadataId =
  356. DbreMetadata.createIdentifier(managedEntity.getName(),
  357. PhysicalTypeIdentifier.getPath(managedEntity.getDeclaredByMetadataId()));
  358. final DbreMetadata dbreMetadata = (DbreMetadata) getMetadataService().get(declaredByMetadataId);
  359. if (dbreMetadata == null || !dbreMetadata.isAutomaticallyDelete()) {
  360. return false;
  361. }
  362. // Check whether the type's annotations have been customised
  363. if (!hasStandardEntityAnnotations(managedEntity)) {
  364. return false;
  365. }
  366. // Finally, check for added constructors, fields and methods
  367. return managedEntity.getDeclaredConstructors().isEmpty()
  368. && managedEntity.getDeclaredFields().isEmpty()
  369. && managedEntity.getDeclaredMethods().isEmpty();
  370. }
  371. private boolean isIdentifierDeletable(final JavaType identifierType) {
  372. final PhysicalTypeMetadata governorPhysicalTypeMetadata =
  373. getPhysicalTypeMetadata(identifierType);
  374. if (governorPhysicalTypeMetadata == null) {
  375. return false;
  376. }
  377. // Check for added constructors, fields and methods
  378. final ClassOrInterfaceTypeDetails managedIdentifier =
  379. governorPhysicalTypeMetadata.getMemberHoldingTypeDetails();
  380. return managedIdentifier.getDeclaredConstructors().isEmpty()
  381. && managedIdentifier.getDeclaredFields().isEmpty()
  382. && managedIdentifier.getDeclaredMethods().isEmpty();
  383. }
  384. private void manageIdentifier(final JavaType javaType,
  385. final AnnotationMetadataBuilder jpaAnnotationBuilder,
  386. final Set<JavaSymbolName> attributesToDeleteIfPresent, final Table table) {
  387. final JavaType identifierType = getIdentifierType(javaType);
  388. final PhysicalTypeMetadata identifierPhysicalTypeMetadata =
  389. getPhysicalTypeMetadata(identifierType);
  390. // Process primary keys and add 'identifierType' attribute
  391. final int pkCount = table.getPrimaryKeyCount();
  392. if (pkCount == 1) {
  393. // Table has one primary key
  394. // Check for redundant, managed identifier class and delete if found
  395. if (isIdentifierDeletable(identifierType)) {
  396. deleteJavaType(identifierType, "the " + table.getName() + " table has only one primary key");
  397. }
  398. attributesToDeleteIfPresent.add(new JavaSymbolName(IDENTIFIER_TYPE));
  399. // We don't need a PK class
  400. final List<Identifier> identifiers = getIdentifiersFromPrimaryKeys(table);
  401. identifierResults.put(javaType, identifiers);
  402. } else if (pkCount == 0 || pkCount > 1) {
  403. // Table has either no primary keys or more than one primary key so
  404. // create a composite key
  405. // Check if identifier class already exists and if not, create it
  406. if (identifierPhysicalTypeMetadata == null || !identifierPhysicalTypeMetadata.isValid()
  407. || identifierPhysicalTypeMetadata.getMemberHoldingTypeDetails() == null) {
  408. createIdentifierClass(identifierType);
  409. }
  410. jpaAnnotationBuilder.addClassAttribute(IDENTIFIER_TYPE, identifierType);
  411. // We need a PK class, so we tell the IdentifierMetadataProvider via
  412. // IdentifierService the various column names, field types and field
  413. // names to use
  414. // For tables with no primary keys, create a composite key using all
  415. // the table's columns
  416. final List<Identifier> identifiers =
  417. pkCount == 0 ? getIdentifiersFromColumns(table) : getIdentifiersFromPrimaryKeys(table);
  418. identifierResults.put(identifierType, identifiers);
  419. }
  420. }
  421. private void notify(final List<ClassOrInterfaceTypeDetails> entities) {
  422. for (final ClassOrInterfaceTypeDetails managedIdentifierType : getManagedIdentifiers()) {
  423. final MetadataItem metadataItem =
  424. getMetadataService().evictAndGet(managedIdentifierType.getDeclaredByMetadataId());
  425. if (metadataItem != null) {
  426. notifyIfRequired(metadataItem);
  427. }
  428. }
  429. for (final ClassOrInterfaceTypeDetails entity : entities) {
  430. final MetadataItem metadataItem =
  431. getMetadataService().evictAndGet(entity.getDeclaredByMetadataId());
  432. if (metadataItem != null) {
  433. notifyIfRequired(metadataItem);
  434. }
  435. }
  436. }
  437. public void onFileEvent(final FileEvent fileEvent) {
  438. if (fileEvent.getFileDetails().getCanonicalPath().endsWith(DBRE_XML)) {
  439. final FileOperation operation = fileEvent.getOperation();
  440. if (operation == FileOperation.UPDATED || operation == FileOperation.CREATED) {
  441. deserializeDatabase();
  442. }
  443. }
  444. }
  445. private void reverseEngineer(final Database database) {
  446. final Set<ClassOrInterfaceTypeDetails> managedEntities =
  447. getTypeLocationService().findClassesOrInterfaceDetailsWithAnnotation(ROO_DB_MANAGED);
  448. // Lookup the relevant destination package if not explicitly given
  449. final JavaPackage destinationPackage = getDestinationPackage(database, managedEntities);
  450. // Set the destination package in the database
  451. database.setDestinationPackage(destinationPackage);
  452. // Get tables from database
  453. final Set<Table> tables = new LinkedHashSet<Table>(database.getTables());
  454. // Manage existing entities with @RooDbManaged annotation
  455. for (final ClassOrInterfaceTypeDetails managedEntity : managedEntities) {
  456. // Remove table from set as each managed entity is processed.
  457. // The tables that remain in the set will be used for creation of
  458. // new entities later
  459. final Table table = updateOrDeleteManagedEntity(managedEntity, database);
  460. if (table != null) {
  461. tables.remove(table);
  462. }
  463. }
  464. // Create new entities from tables
  465. final List<ClassOrInterfaceTypeDetails> newEntities =
  466. new ArrayList<ClassOrInterfaceTypeDetails>();
  467. for (final Table table : tables) {
  468. // Don't create types from join tables in many-to-many associations
  469. if (!table.isJoinTable()) {
  470. JavaPackage schemaPackage = destinationPackage;
  471. if (database.hasMultipleSchemas()) {
  472. schemaPackage =
  473. new JavaPackage(destinationPackage.getFullyQualifiedPackageName() + "."
  474. + DbreTypeUtils.suggestPackageName(table.getSchema().getName()));
  475. }
  476. final JavaType javaType =
  477. DbreTypeUtils.suggestTypeNameForNewTable(table.getName(), schemaPackage);
  478. if (getTypeLocationService().getTypeDetails(javaType) == null) {
  479. table.setIncludeNonPortableAttributes(database.isIncludeNonPortableAttributes());
  480. table.setDisableVersionFields(database.isDisableVersionFields());
  481. table.setDisableGeneratedIdentifiers(database.isDisableGeneratedIdentifiers());
  482. newEntities.add(createNewManagedEntityFromTable(javaType, table));
  483. }
  484. }
  485. }
  486. // Create repositories if required
  487. if (database.isRepository()) {
  488. for (final ClassOrInterfaceTypeDetails entity : newEntities) {
  489. final JavaType type = entity.getType();
  490. getRepositoryJpaOperations().addRepository(
  491. new JavaType(type.getFullyQualifiedTypeName() + "Repository"), type, null);
  492. }
  493. }
  494. // Create services if required
  495. if (database.isService()) {
  496. for (final ClassOrInterfaceTypeDetails entity : newEntities) {
  497. final JavaType type = entity.getType();
  498. final String typeName = type.getFullyQualifiedTypeName();
  499. getServiceOperations().addService(type, new JavaType(typeName + "Service"), null);
  500. }
  501. }
  502. // Create integration tests if required
  503. if (database.isTestAutomatically()) {
  504. for (final ClassOrInterfaceTypeDetails entity : newEntities) {
  505. getIntegrationTestOperations().newIntegrationTest(entity.getType());
  506. }
  507. }
  508. // Notify
  509. final List<ClassOrInterfaceTypeDetails> allEntities =
  510. new ArrayList<ClassOrInterfaceTypeDetails>();
  511. allEntities.addAll(newEntities);
  512. allEntities.addAll(managedEntities);
  513. notify(allEntities);
  514. }
  515. private Table updateOrDeleteManagedEntity(final ClassOrInterfaceTypeDetails managedEntity,
  516. final Database database) {
  517. // Update the attributes of the existing JPA-related annotation
  518. final AnnotationMetadata jpaAnnotation = getJpaAnnotation(managedEntity);
  519. Validate.validState(jpaAnnotation != null, "@%s not found on existing DBRE-managed entity %s",
  520. ROO_JPA_ENTITY.getSimpleTypeName(), managedEntity.getName().getFullyQualifiedTypeName());
  521. // Find table in database using 'table' and 'schema' attributes from the
  522. // JPA annotation
  523. final AnnotationAttributeValue<?> tableAttribute =
  524. jpaAnnotation.getAttribute(new JavaSymbolName("table"));
  525. final String errMsg =
  526. "Unable to maintain database-managed entity "
  527. + managedEntity.getName().getFullyQualifiedTypeName()
  528. + " because its associated table could not be found";
  529. Validate.notNull(tableAttribute, errMsg);
  530. final String tableName = (String) tableAttribute.getValue();
  531. Validate.notBlank(tableName, errMsg);
  532. final AnnotationAttributeValue<?> schemaAttribute =
  533. jpaAnnotation.getAttribute(new JavaSymbolName("schema"));
  534. final String schemaName = schemaAttribute != null ? (String) schemaAttribute.getValue() : null;
  535. final Table table = database.getTable(tableName, schemaName);
  536. if (table == null) {
  537. // Table is missing and probably has been dropped so delete managed
  538. // type and its identifier if applicable
  539. deleteManagedType(managedEntity, "no database table called '" + tableName + "'");
  540. return null;
  541. }
  542. table.setIncludeNonPortableAttributes(database.isIncludeNonPortableAttributes());
  543. table.setDisableVersionFields(database.isDisableVersionFields());
  544. table.setDisableGeneratedIdentifiers(database.isDisableGeneratedIdentifiers());
  545. // Update the @RooJpaEntity attributes
  546. final AnnotationMetadataBuilder jpaAnnotationBuilder =
  547. new AnnotationMetadataBuilder(jpaAnnotation);
  548. final Set<JavaSymbolName> attributesToDeleteIfPresent = new LinkedHashSet<JavaSymbolName>();
  549. manageIdentifier(managedEntity.getName(), jpaAnnotationBuilder, attributesToDeleteIfPresent,
  550. table);
  551. // Manage versionField attribute
  552. final AnnotationAttributeValue<?> versionFieldAttribute =
  553. jpaAnnotation.getAttribute(new JavaSymbolName(VERSION_FIELD));
  554. if (versionFieldAttribute == null) {
  555. if (hasVersionField(table)) {
  556. attributesToDeleteIfPresent.add(new JavaSymbolName(VERSION_FIELD));
  557. } else {
  558. jpaAnnotationBuilder.addStringAttribute(VERSION_FIELD, "");
  559. }
  560. } else {
  561. final String versionFieldValue = (String) versionFieldAttribute.getValue();
  562. if (hasVersionField(table)
  563. && (StringUtils.isBlank(versionFieldValue) || VERSION.equals(versionFieldValue))) {
  564. attributesToDeleteIfPresent.add(new JavaSymbolName(VERSION_FIELD));
  565. }
  566. }
  567. final AnnotationAttributeValue<?> sequenceNameFieldAttribute =
  568. jpaAnnotation.getAttribute(new JavaSymbolName(SEQUENCE_NAME_FIELD));
  569. if (sequenceNameFieldAttribute == null) {
  570. if (!table.isDisableGeneratedIdentifiers()) {
  571. attributesToDeleteIfPresent.add(new JavaSymbolName(SEQUENCE_NAME_FIELD));
  572. } else {
  573. jpaAnnotationBuilder.addStringAttribute(SEQUENCE_NAME_FIELD, "");
  574. }
  575. } else {
  576. final String sequenceNameFieldValue = (String) sequenceNameFieldAttribute.getValue();
  577. if (!table.isDisableGeneratedIdentifiers() && ("".equals(sequenceNameFieldValue))) {
  578. attributesToDeleteIfPresent.add(new JavaSymbolName(SEQUENCE_NAME_FIELD));
  579. }
  580. }
  581. // Update the annotation on disk
  582. final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
  583. new ClassOrInterfaceTypeDetailsBuilder(managedEntity);
  584. cidBuilder.updateTypeAnnotation(jpaAnnotationBuilder.build(), attributesToDeleteIfPresent);
  585. getTypeManagementService().createOrUpdateTypeOnDisk(cidBuilder.build());
  586. return table;
  587. }
  588. /**
  589. * Method to get DbreModelService Service implementation
  590. *
  591. * @return
  592. */
  593. public DbreModelService getDbreModelService() {
  594. if (dbreModelService == null) {
  595. // Get all Services implement DbreModelService interface
  596. try {
  597. ServiceReference<?>[] references =
  598. context.getAllServiceReferences(DbreModelService.class.getName(), null);
  599. for (ServiceReference<?> ref : references) {
  600. dbreModelService = (DbreModelService) context.getService(ref);
  601. return dbreModelService;
  602. }
  603. return null;
  604. } catch (InvalidSyntaxException e) {
  605. LOGGER.warning("Cannot load DbreModelService on DbreDatabaseListenerImpl.");
  606. return null;
  607. }
  608. } else {
  609. return dbreModelService;
  610. }
  611. }
  612. /**
  613. * Method to get FileManager Service implementation
  614. *
  615. * @return
  616. */
  617. public FileManager getFileManager() {
  618. if (fileManager == null) {
  619. // Get all Services implement FileManager interface
  620. try {
  621. ServiceReference<?>[] references =
  622. context.getAllServiceReferences(FileManager.class.getName(), null);
  623. for (ServiceReference<?> ref : references) {
  624. fileManager = (FileManager) context.getService(ref);
  625. return fileManager;
  626. }
  627. return null;
  628. } catch (InvalidSyntaxException e) {
  629. LOGGER.warning("Cannot load FileManager on DbreDatabaseListenerImpl.");
  630. return null;
  631. }
  632. } else {
  633. return fileManager;
  634. }
  635. }
  636. /**
  637. * Method to get IntegrationTestOperations Service implementation
  638. *
  639. * @return
  640. */
  641. public IntegrationTestOperations getIntegrationTestOperations() {
  642. if (integrationTestOperations == null) {
  643. // Get all Services implement IntegrationTestOperations interface
  644. try {
  645. ServiceReference<?>[] references =
  646. context.getAllServiceReferences(IntegrationTestOperations.class.getName(), null);
  647. for (ServiceReference<?> ref : references) {
  648. integrationTestOperations = (IntegrationTestOperations) context.getService(ref);
  649. return integrationTestOperations;
  650. }
  651. return null;
  652. } catch (InvalidSyntaxException e) {
  653. LOGGER.warning("Cannot load IntegrationTestOperations on DbreDatabaseListenerImpl.");
  654. return null;
  655. }
  656. } else {
  657. return integrationTestOperations;
  658. }
  659. }
  660. /**
  661. * Method to get ProjectOperations Service implementation
  662. *
  663. * @return
  664. */
  665. public ProjectOperations getProjectOperations() {
  666. if (projectOperations == null) {
  667. // Get all Services implement ProjectOperations interface
  668. try {
  669. ServiceReference<?>[] references =
  670. context.getAllServiceReferences(ProjectOperations.class.getName(), null);
  671. for (ServiceReference<?> ref : references) {
  672. projectOperations = (ProjectOperations) context.getService(ref);
  673. return projectOperations;
  674. }
  675. return null;
  676. } catch (InvalidSyntaxException e) {
  677. LOGGER.warning("Cannot load ProjectOperations on DbreDatabaseListenerImpl.");
  678. return null;
  679. }
  680. } else {
  681. return projectOperations;
  682. }
  683. }
  684. /**
  685. * Method to get RepositoryJpaOperations Service implementation
  686. *
  687. * @return
  688. */
  689. public RepositoryJpaOperations getRepositoryJpaOperations() {
  690. if (repositoryJpaOperations == null) {
  691. // Get all Services implement RepositoryJpaOperations interface
  692. try {
  693. ServiceReference<?>[] references =
  694. context.getAllServiceReferences(RepositoryJpaOperations.class.getName(), null);
  695. for (ServiceReference<?> ref : references) {
  696. repositoryJpaOperations = (RepositoryJpaOperations) context.getService(ref);
  697. return repositoryJpaOperations;
  698. }
  699. return null;
  700. } catch (InvalidSyntaxException e) {
  701. LOGGER.warning("Cannot load RepositoryJpaOperations on DbreDatabaseListenerImpl.");
  702. return null;
  703. }
  704. } else {
  705. return repositoryJpaOperations;
  706. }
  707. }
  708. /**
  709. * Method to get ServiceOperations Service implementation
  710. *
  711. * @return
  712. */
  713. public ServiceOperations getServiceOperations() {
  714. if (serviceOperations == null) {
  715. // Get all Services implement ServiceOperations interface
  716. try {
  717. ServiceReference<?>[] references =
  718. context.getAllServiceReferences(ServiceOperations.class.getName(), null);
  719. for (ServiceReference<?> ref : references) {
  720. serviceOperations = (ServiceOperations) context.getService(ref);
  721. return serviceOperations;
  722. }
  723. return null;
  724. } catch (InvalidSyntaxException e) {
  725. LOGGER.warning("Cannot load ServiceOperations on DbreDatabaseListenerImpl.");
  726. return null;
  727. }
  728. } else {
  729. return serviceOperations;
  730. }
  731. }
  732. /**
  733. * Method to get Shell Service implementation
  734. *
  735. * @return
  736. */
  737. public Shell getShell() {
  738. if (shell == null) {
  739. // Get all Services implement Shell interface
  740. try {
  741. ServiceReference<?>[] references =
  742. context.getAllServiceReferences(Shell.class.getName(), null);
  743. for (ServiceReference<?> ref : references) {
  744. shell = (Shell) context.getService(ref);
  745. return shell;
  746. }
  747. return null;
  748. } catch (InvalidSyntaxException e) {
  749. LOGGER.warning("Cannot load Shell on DbreDatabaseListenerImpl.");
  750. return null;
  751. }
  752. } else {
  753. return shell;
  754. }
  755. }
  756. /**
  757. * Method to get TypeLocationService Service implementation
  758. *
  759. * @return
  760. */
  761. public TypeLocationService getTypeLocationService() {
  762. if (typeLocationService == null) {
  763. // Get all Services implement TypeLocationService interface
  764. try {
  765. ServiceReference<?>[] references =
  766. context.getAllServiceReferences(TypeLocationService.class.getName(), null);
  767. for (ServiceReference<?> ref : references) {
  768. typeLocationService = (TypeLocationService) context.getService(ref);
  769. return typeLocationService;
  770. }
  771. return null;
  772. } catch (InvalidSyntaxException e) {
  773. LOGGER.warning("Cannot load TypeLocationService on DbreDatabaseListenerImpl.");
  774. return null;
  775. }
  776. } else {
  777. return typeLocationService;
  778. }
  779. }
  780. /**
  781. * Method to get TypeManagementService Service implementation
  782. *
  783. * @return
  784. */
  785. public TypeManagementService getTypeManagementService() {
  786. if (typeManagementService == null) {
  787. // Get all Services implement TypeManagementService interface
  788. try {
  789. ServiceReference<?>[] references =
  790. context.getAllServiceReferences(TypeManagementService.class.getName(), null);
  791. for (ServiceReference<?> ref : references) {
  792. typeManagementService = (TypeManagementService) context.getService(ref);
  793. return typeManagementService;
  794. }
  795. return null;
  796. } catch (InvalidSyntaxException e) {
  797. LOGGER.warning("Cannot load TypeManagementService on DbreDatabaseListenerImpl.");
  798. return null;
  799. }
  800. } else {
  801. return typeManagementService;
  802. }
  803. }
  804. }