close

Filter

loading table of contents...

Studio Developer Manual / Version 2207

Table Of Contents

9.31.2 Implementing the Java Backend

Let's start with implementing a so called EntityController class. An instance of EntityController is created for every remote bean that is created in Studio via the following call:

beanFactory.getRemoteBean(...)

EntityControllers are used when you have multiple elements of the same type in Studio, Content instances in Studio, for example, are created through EntityControllers. The same applies for messages of the notification API or CMS users and user groups.

In this example, you will create entities that represent notes created by users. The user should be able to create, update and delete notes.

The note model would look like this:

public class Note {
  private String description;
  private String owner;
  private String noteId;

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public String getOwner() {
    return owner;
  }

  public void setOwner(String owner) {
    this.owner = owner;
  }

  public String getNoteId() {
    return noteId;
  }

  public void setNoteId(String noteId) {
    this.noteId = noteId;
  }
}

Example 9.128. Note model


You also need a representation class for this model (the reason for this will be explained later).

public class NoteRepresentation {
  private String description;
  private String owner;
  private String noteId;

  NoteRepresentation(Note note) {
    this.description = note.getDescription();
    this.owner = note.getOwner();
    this.noteId = note.getNoteId();
  }

  public String getDescription() {
    return description;
  }

  public String getOwner() {
    return owner;
  }

  public String getNoteId() {
    return noteId;
  }
}

Example 9.129. Representation class for note model


You also have a service which handles the notes:

@DefaultAnnotation(NonNull.class)
public class NotesService {

  private final List<Note> list;

  public NotesService() {
    list = new ArrayList<>();
    Note dummy1 = new Note();
    dummy1.setOwner("me");
    dummy1.setNoteId("1");
    dummy1.setDescription("I have to write a real storage for this!");
    Note dummy2 = new Note();
    dummy2.setOwner("me");
    dummy2.setNoteId("2");
    dummy2.setDescription("And a lot of other stuff too!");
    list.add(dummy1);
    list.add(dummy2);
  }

  @Nullable
  public Note getNote(String id) {
    return list.stream().filter(note -> id.equals(note.getNoteId())).findFirst().orElse(null);
  }

  public boolean deleteNote(String id) {
    Note noteToDelete = getNote(id);
    if (noteToDelete == null) {
      return false;
    }
    list.retainAll(
            list.stream().filter(note -> note != noteToDelete).collect(Collectors.toList())
    );
    return true;
  }

  @Nullable
  public Note updateNote(String id, String description) {
    Note note = getNote(id);
    if (note == null) {
      return null;
    }
    note.setDescription(description);
    return note;
  }

  public List<Note> getNotes() {
    return Collections.unmodifiableList(list);
  }

  public void setNotes(List<Note> notes) {
    list.clear();
    list.addAll(notes);
  }
}

Example 9.130. Service for note handling


So you have a note with a description, an owner and an ID and a service you can query for notes. You now have to create the EntityController class that wraps the REST operations around it:

@RestController
@RequestMapping(value = "notes/note/{" + NoteEntityController.PATH_PARAM_ID + "}", produces = MediaType.APPLICATION_JSON_VALUE)
public class NoteEntityController implements EntityController<Note> {
  public static final String PATH_PARAM_ID = "id";

  private final NotesService notesService;

  public NoteEntityController(NotesService notesService) {
    this.notesService = notesService;
  }

  @Override
  public Note getEntity(@NonNull Map<String, String> params) {
    return notesService.getNote(params.get(PATH_PARAM_ID));
  }

  @NonNull
  @Override
  public Map<String, String> getPathVariables(@NonNull Note entity) {
    HashMap<String, String> map = new HashMap<>();
    map.put(PATH_PARAM_ID, entity.getNoteId());
    return map;
  }

  @GetMapping
  public ResponseEntity<NoteRepresentation> getRepresentation(@PathVariable Map<String, String> params) {
    Note note = getEntity(params);
    if (note == null) {
      return ResponseEntity.notFound().build();
    }
    return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(new NoteRepresentation(note));
  }

  @DeleteMapping
  public boolean delete(@PathVariable Map<String, String> params) {
    Note note = getEntity(params);
    if (note == null) {
      return false;
    }
    return notesService.deleteNote(note.getNoteId());
  }

  @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<NoteRepresentation> setProperties(@PathVariable final Map<String, String> params,
                                                          @RequestBody final Map<String, Object> json) {
    String description = (String) json.get("description");
    Note note = getEntity(params);
    if (note == null) {
      return ResponseEntity.notFound().build();
    }
    Note updatedNote = notesService.updateNote(note.getNoteId(), description);
    if (updatedNote == null) {
      return ResponseEntity.badRequest().build();
    }
    return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(new NoteRepresentation(updatedNote));
  }
}

Example 9.131. Entity Controller class for TEST operations


Have a look on the class NoteEntityController in detail:

@RestController
@RequestMapping(value = "notes/note/{" + NoteEntityController.PATH_PARAM_ID + "}", produces = MediaType.APPLICATION_JSON_VALUE)

Example 9.132. Annotation for bean creation


The first two annotations are used to tell Spring what kind of bean you are creating. The first annotation states that the class represents a REST controller. The @RequestMapping annotation provides the REST configuration. The produces value defines that all requests expecting the JSON format will be accepted and the value property tells Spring under which URL the entity can be invoked. Note that the URL can have multiple path parameters. This example shows the most simple form with only one Id parameter.

The class has one REST GET method:

@GetMapping
public ResponseEntity<NoteRepresentation> getRepresentation(@PathVariable Map<String, String> params) {
  Note note = getEntity(params);
  if (note == null) {
    return ResponseEntity.notFound().build();
  }
  return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(new NoteRepresentation(note));
}

Example 9.133. REST GET method of NoteEntityController


So when a GET is executed on this controller, the note NoteRepresentation is returned and serialized to JSON. If the return format should differ from the originating model, you can freely customize the representation class. Because of the automatic REST linking, it is important that you don't return the same class here that has been defined as type of the EntityController! You can put models of other EntityControllers inside your representation as well. These entities will be converted to references during serialization. By this, different EntityControllers can be linked to each other. So you always have to create a representation class for the model that is bound for the EntityController. You just have to make sure that this representation contains the fields that should be supported by the RemoteBean you will implement.

Note

Note

Note that in this example, it is not covered how and where these notes are stored. The methods in NotesService have to be implemented properly to support a real data access layer.

Next, add support for deletion by adding the following method:

@DeleteMapping
public boolean delete(@PathVariable Map<String, String> params) {
  Note note = getEntity(params);
  if (note == null) {
    return false;
  }
  return notesService.deleteNote(note.getNoteId());
}

Example 9.134. Deletion of note in NoteEntityController


The method is pretty simple: if a DELETE request is executed in the controller, the corresponding helper is invoked and the note is deleted.

The same applies for updates:

@PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<NoteRepresentation> setProperties(@PathVariable final Map<String, String> params,
                                                        @RequestBody final Map<String, Object> json) {
  String description = (String) json.get("description");
  Note note = getEntity(params);
  if (note == null) {
    return ResponseEntity.notFound().build();
  }
  Note updatedNote = notesService.updateNote(note.getNoteId(), description);
  if (updatedNote == null) {
    return ResponseEntity.badRequest().build();
  }
  return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(new NoteRepresentation(updatedNote));
}

Example 9.135. Update of note in NoteEntityController


You have finished the Java part now. Finally, you have to declare the entity as bean in the Spring configuration:

@Bean
public NotesService notesService() {
  return new NotesService();
}

@Bean
public NoteEntityController noteEntityController(NotesService notesService) {
  return new NoteEntityController(notesService);
}

Example 9.136. Declare NoteEntityController as bean


You can rebuild the module and restart Studio now. The next steps can be implemented using the incremental Studio build that doesn't require a Studio restart.

Search Results

Table Of Contents
warning

Your Internet Explorer is no longer supported.

Please use Mozilla Firefox, Google Chrome, or Microsoft Edge.