Annotations

There are two groups of annotations specific to the View System: type and field.

Type Annotations

Type annotations are class-level annotations. They typically appear with ViewModel classes, but may be used with other classes or interface implementations.

@HandlebarsTemplate

Identifies the ViewModel as a producer of Handlebars output, and specifies the Handlebars template’s location. This annotation is included in the Brightspot Handlebars plugin.

This annotation requires the @ViewInterface annotation.

import com.psddev.cms.view.PageEntryView;
import com.psddev.cms.view.ViewInterface;
import com.psddev.cms.view.ViewModel;
import com.psddev.handlebars.HandlebarsTemplate;

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> implements PageEntryView {

    public String getBody() {
        return model.getBody();
    }

    public String getHeadline() {
        return model.getHeadline();
    }
}

The above example specifies that for the class ArticleViewModel, the associated Handlebars file is <webroot>/Article.hbs.

For detailed information about Handlebars, see Handlebars.

@JsonView

Declares the ViewModel as a producer of JSON output.

This annotation requires the @ViewInterface annotation.

import com.psddev.cms.view.JsonView;
import com.psddev.cms.view.PageEntryView;
import com.psddev.cms.view.ViewInterface;
import com.psddev.cms.view.ViewModel;

@JsonView
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> implements PageEntryView {

    public String getBody() {
        return model.getBody();
    }

    public String getHeadline() {
        return model.getHeadline();
    }
}

The JSON output is an object with keys corresponding to the Model’s fields and associated values, as in the following example.

{
  "body" : "A platoon of aliens who traveled four light years in suspended animation landed in Times Square Wednesday evening...",
  "headline" : "Crippled by Earth's Gravity, Aliens Demand Submission"
}

@ViewInterface

Invokes a process to transform a Model’s data into the form specified by another annotation, usually @HandlebarsTemplate (for Handlebars output), @JsonView (for JSON output), or @ViewRendererClass (for customized output). See those annotations for examples of how to use @ViewInterface.

@ViewRendererClass

Specifies a class implementing ViewRenderer to render the model’s data.

This annotation requires the @ViewInterface annotation.

The following example comes from Brightspot’s implementation for RawView:

package com.psddev.cms.view;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@ViewInterface
@ViewRendererClass(RawViewRenderer.class)
public interface RawView {

    List<?> getItems();

    /* static methods... */

}

Field Annotations

The following field-level annotations appear before the declaration of individual fields within a ViewModel.

@CurrentSite

Retrieves the current Site object based on the Request URL. This annotation is available only if your Brightspot project is configured as a multi-site instance.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.CurrentSite;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @CurrentSite
   private Site currentSite;

   public Site getCurrentsite() {
      return currentSite;
   }
}

In the previous snippet—

  • Lines 11–12 use the annotation @CurrentSite to retrieve the current site into the variable currentSite.
  • Lines 14–16 return the value for currentSite for use in transformed output.

@HttpCookie

Retrieves the value for a specified cookie. If the specified cookie does not exist in the request, the annotation returns a null value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpCookie;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpCookie("username")
   private String userName;

   public String getUsername() {
      return userName;
   }
}

In the previous snippet—

  • Lines 11–12 specify retrieving the value for the cookie username into the variable userName.
  • Lines 14–16 return the value for userName for use in transformed output.

@HttpHeader

Retrieves the value for a specified field in the HTTP request header. If the specified field does not exist in the request header, the annotation returns a null value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpHeader;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpHeader("User-Agent")
   private String userAgent;

   public String getUseragent() {
      return userAgent;
   }
}

In the previous snippet—

  • Lines 11–12 specify retrieving the value for the header field User-Agent into the variable userAgent.
  • Lines 14–16 return the value for userAgent for use in transformed output.

@HttpMethod

Retrieves the HTTP request method. Typical values for the request method are GET and POST. For a listing of possible HTTP request methods, see HTTP request methods.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpMethod;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpMethod
   private String httpMethod;

   public String getHttpmethod() {
      return httpMethod;
   }
}

In the previous snippet—

  • Lines 11–12 use the annotation @HttpMethod to retrieve the request method into the variable httpMethod.
  • Lines 14–16 return the value for httpMethod for use in transformed output.

@HttpParameter

Retrieves the value of a requested parameter passed in a URL’s query string or in a posted form. For example, if the URL is http://www.example.com/index.html?userid=5, you can use this annotation to retrieve the value for userid. If the parameter does not exist in the query string or form, this annotation returns a null value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpParameter;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpParameter("userid")
   private String userID;

   public String getUserid() {
      return userID;
   }
}

In the previous snippet—

  • Lines 11–12 specify setting the variable userID to the value of the parameter userid.
  • Lines 14–16 return the value for userID for use in transformed output.

@HttpRequestAttribute

Retrieves the object or string representing a requested servlet attribute. If the attribute does not exist, this annotation returns a null value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpRequestAttribute;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpRequestAttribute("currentdate")
   private String currentDate;

   public String getCurrdate() {
      return currentDate;
   }
}

In the previous snippet—

  • Lines 11–12 specify retrieving the value for the attribute currentdate into the variable currentDate.
  • Lines 14–16 return the value for currentDate for use in transformed output.

@HttpServletPath

Returns the servlet path, typically the path appearing after the domain in a URL. For example, in the URL http://www.example.com/solar-system-implodes, the servlet path is /solar-system-implodes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpServletPath;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpServletPath
   private String servletPath;

   public String getServletpath() {
      return servletPath;
   }
}

In the previous snippet—

  • Lines 11–12 use the annotation @HttpServletPath to retrieve the path into the variable servletPath.
  • Lines 14–16 return the value for servletPath for use in transformed output.

@HttpSignedCookie

Retrieves the value for a specified signed cookie. If the specified cookie does not exist in the request, the annotation returns a null value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpSignedCookie;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpSignedCookie("username")
   private String userName;

   public String getUsername() {
      return userName;
   }
}

In the previous snippet—

  • Lines 11–12 specify retrieving the value for the cookie username into the variable userName.
  • Lines 14–16 return the value for userName for use in transformed output.

@HttpStorageItemParameter

Transforms a submitted file into a StorageItem, and associates the value for name with the StorageItem. The files can be uploaded files or JSON representations of files.

For example, the following HTML snippet submits two images to the server, one as a file and another in JSON format.

1
2
3
4
<html>
   <input type="file" name="image" />
   <input type="hidden" value="{JSON structure here}" name="imageJson" />
</html>

The following ViewModel snippet transforms the uploaded files into StorageItems.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.HttpStorageItemParameter;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @HttpStorageItemParameter("image")
   private StorageItem profileImage;

   @HttpStorageItemParameter("imageJson")
   private StorageItem profileImageJson;

}

In the previous snippet—

  • Lines 11–12 use the annotation @HttpStorageItemParameter to transform the file submitted in an <input> tag with name image and then to save that file in a variable named profileImage.
  • Lines 14–15 use the annotation @HttpStorageItemParameter to transform the JSON structure submitted in an <input> tag with name imageJson and then to save that file in a variable named profileImageJson.

@MainObject

Retrieves the object associated with a published permalink. For example, if the permalink associated with an article is http://localhost/gravity-canceled-today, this annotation returns the object associated with /gravity-canceled-today.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package content.article;

import com.psddev.cms.view.servlet.MainObject;

/* Other imports... */

@HandlebarsTemplate("Article")
@ViewInterface
public class ArticleViewModel extends ViewModel<Article> {

   @MainObject
   private String mainObject;

   public String getMainobjectname() {
      return mainObject.getClass().getName();
   }
}

In the previous snippet—

  • Lines 11–12 use the annotation @MainObject to retrieve the object into the variable mainObject.
  • Lines 14–16 return the main object’s class name for use in transformed output.

Custom Field Annotations

Custom ViewModel field annotations are a mechanism for populating fields on a ViewModel before any of your ViewModel logic executes. Currently, ServletViewRequestAnnotationProcessor is the only interface which allows you to populate fields on your ViewModel. If you need to access the HttpServletRequest on your ViewModel, then as a best practice you should create a custom annotation.

The simplest pattern for creating custom ViewModel annotations is to implement the annotation in a class and then apply the annotation to a ViewModel’s property or method. For many examples of advanced implementations of ViewModel annotations, including standalone annotation processors, see the Brightspot repository’s servlet directory.

In the following sample, you develop a custom ViewModel annotation that checks if the current user is logged in before displaying an article’s body. If the user is not logged in, the ViewModel displays an appropriate message.

Step 1: Define Annotation

In a file CurrentUser.java, enter the following text:

 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
29
30
31
32
33
34
35
36
37
38
39
40
package annotations;

import com.psddev.cms.view.servlet.ServletViewRequestAnnotationProcessor;
import com.psddev.cms.view.servlet.ServletViewRequestAnnotationProcessorClass;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import yourpackage.User; /* User-defined class */

import java.lang.annotation.*;

@ServletViewRequestAnnotationProcessorClass(CurrentUserProcessor.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CurrentUser {
}

class CurrentUserProcessor implements ServletViewRequestAnnotationProcessor<CurrentUser> {

    @Override
    public Object getValue(HttpServletRequest request, String fieldName, CurrentUser annotation) {

        Cookie[] cookies = request.getCookies();

        for (Cookie cookie : cookies ) {

            if (cookie.getName().equals("authenticationCookieName")) {
                if (passesAuthenticatonTest(cookie.getValue())) {
                    User currentUser = lookupUserByToken(cookie);
                    return currentUser;
                } else {
                    return null;
                }
            }
        }

        return null;
    }
}

In the previous snippet—

  • Lines 13–17 declare the @CurrentUser annotation as applicable to fields.
  • Lines 19–40 implement the annotation’s processor as an implementation of ServletViewRequestAnnotationProcessor:
    • Line 24 retrieves the cookies provided by HTTP request.
    • Lines 26–36 loop over all the cookies.
    • Line 28 processes the cookie authenticationCookieName, which is present only if the user is logged in.
    • Line 29 checks if the authentication cookie is valid. passesAuthenticatonTest is a user-defined function.
    • Line 30 retrieves the user record associated with the authentication cookie. lookupUserByToken is a user-defined function.

Step 2: Apply Annotation to ViewModel

In the ViewModel, enter the following text:

 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
package content.article;

import annotations.CurrentUser;
import yourpackage.User; /* User-defined class */
import com.psddev.cms.view.ViewModel;
import com.psddev.cms.view.PageEntryView;

import styleguide.content.article.ArticleView;

public class ArticleViewModel extends ViewModel<Article> implements ArticleView, PageEntryView {

    @CurrentUser
    private User currentUser;

    @Override
    public String getBody() {
        if (currentUser == null) {
            return "Please log in or register to read this story.";
        } else {
            return model.getBody();
        }
    }

    /* Other Getters */
}

In the previous snippet—

  • Line 12 applies the customized annotation @CurrentUser to the field currentUser:
    • If the user is logged in, the annotation initializes the field to the current user’s User record.
    • If the user is not logged in, the annotation initializes the field to null.
  • Lines 15–22 implement the getBody method:
    • If currentUser is null, the user is not logged in and the ViewModel displays a prompt.
    • If currentUser is not null, the ViewModel displays the article’s body.
../../../_images/custom-viewmodel-annotation.svg

See also: