Zadanie 3 — generovanie kódu a AOP

Ciele

  1. Precvičiť generovanie kódu počas kompilácie aplikácie.
  2. Naučiť sa používať aspektovo-orientované programovanie a AspectJ.

Úloha 1: Generovanie kódu

Aby sme optimalizovali prácu našej knižnice, presunieme čo najviac operácii do fázy prekladu. Okrem SQL príkazov na vytvárania tabuliek, budeme generovať aj zdrojový kód pre načítavanie a ukladanie jednotlivých typov entít.

Pomocou anotačného procesora vygenerujte pre každý entítny typ novú triedu, ktorá bude zabezpečovať perzistenciu práve tohto typu — data access object (DAO). Vygenerujte tiež novú implementáciu rozhrania PersistenceManager, ktorá bude len na základe typu argumentu volať metódy príslušného DAO. Táto trieda musí byť nazvaná GeneratedPersistenceManager.

Upravte PersistenceManagerFactory, aby vytvárala inštanciu GeneratedPersistenceManager:

public class PersistenceManagerFactory {
    private PersistenceManagerFactory() {}

    public static PersistenceManager create(Connection connection) {
        try {
            Class<?> managerClass = Class.forName(
                    "sk.tuke.meta.persistence.GeneratedPersistenceManager");
            Constructor<?> constructor = managerClass.getConstructor(Connection.class);
            return (PersistenceManager) constructor.newInstance(connection);
        } catch (ClassNotFoundException | NoSuchMethodException |
                 InstantiationException | IllegalAccessException |
                 InvocationTargetException e) {
            throw new PersistenceException("Cannot create PersistenceManager", e);
        }
    }
}

Využívajte možnosti oddelenia všeobecného kódu od špecifického. Napríklad umiestníte všeobecný kód do abstraktných tried, od ktorých budú generované triedy dediť.

Schéma vzťahov generovaného kódu
Obr. 1: Schéma vzťahov generovaného kódu

Úloha 2: Podpora transakcií

V kóde, ktorý bude používať našu knižnicu môže byť potrebné použiť transakcie. Napríklad ak niekoľko operácií nad databázou spolu súvisa a musia sa teda vykonať naraz, inak by sa databáza na chvíľu dostala do nekonzistentného stavu.

Definujte anotáciu @AtomicPersistenceOperation v balíku sk.tuke.meta.persistence.annotations pomocou ktorej bude možné označiť používateľské metódy, ktoré budú zodpovedať jednej transakcii.

Pomocou AspectJ implementujte riešenie, ktoré automaticky vytvori transakciu pre takto označené metódy. Na konci ich vykonania metódy sa vykoná commit alebo rollback podľa toho, či metóda vyhodila výnimku. Nekonzistenciu hodnôt objektov po rollbacku riešiť netreba.

Poznámka

Pre jednoduchosť môžeme predpokladať, že v aplikácii bude existovať iba jedna inštancia triedy PersistenceManager. Pomocou aspektu môžete zachytiť vytváranie tejto inštancie.

Organizácia projektu

Pridajte nový modul pre aspekty s názvom orm-aspects. Jeho pom.xml môže vyzerať následovne:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>sk.tuke.meta.orm</groupId>
    <artifactId>assignment-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>orm-aspects</artifactId>

  <dependencies>
    <dependency>
      <groupId>sk.tuke.meta.orm</groupId>
      <artifactId>orm-annotations</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>sk.tuke.meta.orm</groupId>
      <artifactId>orm-runtime</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>dev.aspectj</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

Modul example-app bude využívať aspekty definované v orm-aspects. Preto do jeho pom.xml doplňme závislosť a definíciu aspektovej knižnice:

  <dependencies>

    ...

    <!-- ORM aspects -->
    <dependency>
      <groupId>sk.tuke.meta.orm</groupId>
      <artifactId>orm-aspects</artifactId>
      <version>${project.version}</version>
    </dependency>

    ...

  </dependencies>

  <build>
    <finalName>app</finalName>
    <plugins>

      ...

      <!-- Configure AspectJ weaving -->
      <plugin>
        <groupId>dev.aspectj</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <configuration>
        <aspectLibraries>
            <aspectLibrary>
              <groupId>sk.tuke.meta.orm</groupId>
              <artifactId>orm-aspects</artifactId>
            </aspectLibrary>
          </aspectLibraries>
        </configuration>
      </plugin>

      ...

    </plugins>
  </build>

Na koniec upravíme aj koreňový pom.xml. Pridáme nový modul:

  <modules>
    <module>orm-runtime</module>
    <module>orm-annotations</module>
    <module>orm-processor</module>
    <module>orm-aspects</module>    <!-- Pridaný modul s aspektmi -->
    <module>example-app</module>
  </modules>

A doplníme konfiguráciu pluginu aspectj-maven-plugin. Ta tam už je, takže je potrebné ju doplniť:

        <!-- AspectJ (for iteration 3) -->
        <plugin>
          <groupId>dev.aspectj</groupId>
          <artifactId>aspectj-maven-plugin</artifactId>
          <version>1.14.1</version>
          <!-- ZAČIATOK NOVEJ KONFIGURACIE -->
          <configuration>
            <complianceLevel>${maven.compiler.release}</complianceLevel>
          </configuration>
          <executions>
            <execution>
              <id>compile-with-aspectj</id>
              <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
              </goals>
            </execution>
          </executions>
          <dependencies>
            <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjtools</artifactId>
              <version>1.9.21</version>
            </dependency>
          </dependencies>
          <!-- KONIEC NOVEJ KONFIGURACIE -->
        </plugin>

Odovzdanie

Zadanie je potrebné vypracovať v priradenom projekte v Gitlabe do konca 10. mája.