Studio Developer Manual / Version 2404
Table Of Contents
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.116. 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.117. 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.118. 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.119. 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.120. 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.121. 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 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.122. 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.123. 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.124. 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.