Augmentations

Augmentations enable adding implementations of interfaces to an existing class that you cannot modify (for example, the source comes from an external dependency). An augmentation extends the Augmentation class and specifies the interface to be implemented, for example:

public class MyAugmentation extends Augmentation<TargetClass> implements NewInterface {

   public void ImplementedMethod(...) { ... }

   ...
}

The implemented methods in an augmentation extend the functionality of the class that is the target of the augmentation, but the augmentation does not change the target class itself. An augmentation can return an instance of the target class with the Augmentation#getOriginalObject method, allowing the augmentation to invoke methods originally defined on the target class.

Client code can access augmentation methods with the State.as method, which converts the state of an object into an instance of the target augmentation class. To determine if an object’s state can be converted into an instance of the augmentation class, use the State.isInstantiableTo(Class<?>) method.

As an example, consider a new requirement applied to an existing site. Instances of some Content types — Article, Image, and Video — must be downloadable if certain conditions are met. That is, an Article, Image, or Video object can be downloaded if it satisfies a download rule applied to each type. Each type has a different download rule. For the Article type, instances must be available for download 60 days after the publication date. For Image and Video types, different rules determine their availability for download.

To accommodate the requirement, a new interface, Downloadable, must be added, along with three augmentations for the Article, Image, and Video types. Each augmentation will implement Downloadable differently, to reflect the fact that different download rules apply for Article, Image, and Video.

Step 1: Create the Interface

The interface declares one method. Implementations of the isDownloadable method will determine if an Article, Image, or Video object is eligible for download.

public interface Downloadable {

   boolean isDownloadable();
}

Step 2: Create the Augmentations

A different augmentation is required for the Article, Image, and Video types, each of which implements the isDownloadable method differently. The following example shows the implementation for the Article class. (Implementations for Video and Image would be different.) The method returns true if an article was published 60 or more days prior to the current date. Note that the Augmentation#getOriginalObject method is used to get the publish date from the Article object.

import com.psddev.dari.db.Augmentation;

public class ArticleAugmentation extends Augmentation<Article> implements Downloadable {

   public  boolean isDownloadable() {
      Date publishDate = getOriginalObject().getPublishDate();
      Date todayDate = Calendar.getInstance().getTime();
      long duration  = todayDate.getTime() - publishDate.getTime();
      return (TimeUnit.MILLISECONDS.toDays(duration) >= 60);
   }
}

Step 3: Retrieve Augmented Objects

A client can use code similar to the following to identify instances of Content types that are eligible for download. The code snippet queries for instances of the Content type. For each result of the query, the code tests if the object is downloadable; that is, if the class of the object has been augmented with the Downloadable interface. If the object is of type Article, Image, or Video, the object is converted to the applicable augmentation type, and the augmentation’s isDownloadable method is called. If the method returns true, the Article, Image, or Video object is listed in the results with a download link. Otherwise, the object is listed in the results without a download link.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 ...

List<Content> objects = Query.from(Content.class).selectAll();
for (Content object : objects) {
   if (object.isInstantiableTo(Downloadable.class))
   {
      if (object.getClass() == Article.class && object.as(ArticleAugmentation.class).isDownloadable())
      {
         // Invoke code to make Article object downloadable
         continue;
      }

      else if (object.getClass() == Image.class && object.as(ImageAugmentation.class).isDownloadable())
      {
         // Invoke code to make Image object downloadable
         continue;
      }

      else if (object.getClass() == Video.class && object.as(VideoAugmentation.class).isDownloadable())
      {
         // Invoke code to make Video object downloadable
         continue;
      }

      else // Add object to result without download link.
   }
   else // Add object to result without download link.
}

In the previous snippet—

  • Line 3 retrieves all objects that inherit from the Content class, which are then iterated by the for loop that follows.
  • Line 5 tests that the object’s state can be converted into an instance of a Downloadable class.
  • Lines 7–23 determine if the object satisfies the download rules as implemented in the augmentation’s isDownloadable method.