PERSONAL - EIST Cheat Sheet

Table of Contents

Dockerfiles and Paths in Linux

FROM baseimage                  # always first command in a dockerfile
WORKDIR </path>                 # directory where the commands in this file are executed
COPY <src> <dest>               # copies a file, <src> may contain wildcards
RUN <command>                   # runs a command in a shell, usually linux syntax
CMD <command> <param1> <param2> # default for executing container, there may only be one CMD instruction
FROM baseimage                  # always first command in a dockerfile
WORKDIR </path>                 # directory where the commands in this file are executed
COPY <src> <dest>               # copies a file, <src> may contain wildcards
RUN <command>                   # runs a command in a shell, usually linux syntax
CMD <command> <param1> <param2> # default for executing container, there may only be one CMD instruction

Async Messages (RestTemplate, CompleteableFuture)

/*
    STEP 1:
    Send POST-request by creating a corresponding ResponseEntity using postForEntity() of RestTemplate
*/
public void postMethod(Object parameter){
    ResponseEntity<Class> response = rest.postForEntity(URL_STRING, HTTP_ENTITY, CLASS);
}
/*
    STEP 1:
    Send POST-request by creating a corresponding ResponseEntity using postForEntity() of RestTemplate
*/
public void postMethod(Object parameter){
    ResponseEntity<Class> response = rest.postForEntity(URL_STRING, HTTP_ENTITY, CLASS);
}
/*
    STEP 2: 
    Handle POST-requests accordingly using method with same name in a @RestController-Class
*/

@RestController
@RequestMapping(value = "/whatever/", consumes = "application/json")
public class InboxResource {
    @PostMapping("whatever")
    public ResponseEntity<String> postMethod(@RequestBody Object parameter){
        return new ResponseEntity<>(parameter, HttpStatus.OK);
    }
}
/*
    STEP 2: 
    Handle POST-requests accordingly using method with same name in a @RestController-Class
*/

@RestController
@RequestMapping(value = "/whatever/", consumes = "application/json")
public class InboxResource {
    @PostMapping("whatever")
    public ResponseEntity<String> postMethod(@RequestBody Object parameter){
        return new ResponseEntity<>(parameter, HttpStatus.OK);
    }
}
/*
    STEP 3:
    Replace synchronous methods with async counterparts using CompleteableFuture.supplyAsync
*/
// sync version, example given with produce()-method
@PostMapping("whatever")
public ResponseEntity<Object> postMethodOld(@RequestBody Object parameter) {
    return new ResponseEntity<>(factory.produce(parameter), HttpStatus.OK);
}

// async version, example given with produce()-method and string return type
@PostMapping("whatever")
public CompletableFuture<String> postMethodAsync(@RequestBody Object parameter) {
    return CompletableFuture.supplyAsync(() -> factory.produce(parameter).getName());
}
/*
    STEP 3:
    Replace synchronous methods with async counterparts using CompleteableFuture.supplyAsync
*/
// sync version, example given with produce()-method
@PostMapping("whatever")
public ResponseEntity<Object> postMethodOld(@RequestBody Object parameter) {
    return new ResponseEntity<>(factory.produce(parameter), HttpStatus.OK);
}

// async version, example given with produce()-method and string return type
@PostMapping("whatever")
public CompletableFuture<String> postMethodAsync(@RequestBody Object parameter) {
    return CompletableFuture.supplyAsync(() -> factory.produce(parameter).getName());
}
/*
    STEP 3.5:
    Reroute methods in client class.
*/

// sync version
public Object produceSync() {
    var request = createHttpEntity(STRINGNAME);
    return rest.postForObject(BASE_URL + PARAM, request, Object.class);
}

// async version
public void produceAsync(InboxClient client) {
    CompletableFuture<String> message =
            CompletableFuture.supplyAsync(
                    () -> rest.postForObject(BASE_URL + PARAM, createHttpEntity(STRINGNAME), String.class)
            );
    try {
        client.ready(message.get());
    } catch (InterruptedException | ExecutionException e) {
        throw new RuntimeException(e);
    }
}
/*
    STEP 3.5:
    Reroute methods in client class.
*/

// sync version
public Object produceSync() {
    var request = createHttpEntity(STRINGNAME);
    return rest.postForObject(BASE_URL + PARAM, request, Object.class);
}

// async version
public void produceAsync(InboxClient client) {
    CompletableFuture<String> message =
            CompletableFuture.supplyAsync(
                    () -> rest.postForObject(BASE_URL + PARAM, createHttpEntity(STRINGNAME), String.class)
            );
    try {
        client.ready(message.get());
    } catch (InterruptedException | ExecutionException e) {
        throw new RuntimeException(e);
    }
}

REST API w/ Spring Boot

Endpoints (Server-Side)

// GET, POST, PUT and DELETE functions (CRUD)
@GetMapping("endpoint_dir")
public ResponseEntity<List<Object>> getObjects(@RequestParam(name = "name", defaultValue = value)...) {
    return ResponseEntity.ok(objectService.getAllObjects());
}

@PostMapping("endpoint_dir")
public ResponseEntity<Object> createObject(@RequestBody Object object) {
    if (condition) { // most likely object.getId() != null
        return ResponseEntity.badRequest().build();
    }
    return ResponseEntity.ok(objectService.saveObject(object));
}

@PutMapping("endpoint_dir/{object_id}")
public ResponseEntity<Object> updateObject(
    @RequestBody Object updatedObject, 
    @PathVariable("object_id") UUID objectId) {
    if (condition) { // most likely !updatedObject.getId().equals(objectId)
        return ResponseEntity.badRequest().build();
    }
    return ResponseEntity.ok(objectService.saveObject(updatedObject));
}

@DeleteMapping("endpoint_dir/{object_id}")
public ResponseEntity<Void> deleteObject(@PathVariable("object_id") UUID objectId) {
    objectService.deleteObject(objectId);
    return ResponseEntity.noContent().build();
}
// GET, POST, PUT and DELETE functions (CRUD)
@GetMapping("endpoint_dir")
public ResponseEntity<List<Object>> getObjects(@RequestParam(name = "name", defaultValue = value)...) {
    return ResponseEntity.ok(objectService.getAllObjects());
}

@PostMapping("endpoint_dir")
public ResponseEntity<Object> createObject(@RequestBody Object object) {
    if (condition) { // most likely object.getId() != null
        return ResponseEntity.badRequest().build();
    }
    return ResponseEntity.ok(objectService.saveObject(object));
}

@PutMapping("endpoint_dir/{object_id}")
public ResponseEntity<Object> updateObject(
    @RequestBody Object updatedObject, 
    @PathVariable("object_id") UUID objectId) {
    if (condition) { // most likely !updatedObject.getId().equals(objectId)
        return ResponseEntity.badRequest().build();
    }
    return ResponseEntity.ok(objectService.saveObject(updatedObject));
}

@DeleteMapping("endpoint_dir/{object_id}")
public ResponseEntity<Void> deleteObject(@PathVariable("object_id") UUID objectId) {
    objectService.deleteObject(objectId);
    return ResponseEntity.noContent().build();
}

RESTFUL Requests (Client-Side, Controller Class)

private final WebClient webClient; // web client to handle REST requests
private final List<Object> objects; // private, local list of objects to be stored

public ObjectController(){
    this.webClient = WebClient.builder()
            .baseUrl("http://localhost:8080/") // insert URL here
            .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .build();
    this.objects = new ArrayList<>();
}

// POST
public void addObject(Object object, Consumer<List<Object>> objectConsumer) {
    webClient.post()
            .uri("endpoint_dir")
            .bodyValue(object)
            .retrieve()
            .bodyToMono(Object.class)
            .onErrorStop()
            .subscribe(newObject -> {
                objects.add(newObject);
                objectConsumer.accept(objects);
            });
}

// PUT
public void updateObject(Object object, Consumer<List<Object>> objectConsumer) {
    webClient.put()
            .uri("endpoint_dir/" + object.getId())
            .bodyValue(object)
            .retrieve()
            .bodyToMono(Object.class)
            .onErrorStop()
            .subscribe(newObject -> {
                objects.replaceAll(oldObject -> oldObject.getId().equals(newObject.getId()) ? newObject : oldObject);
                objectConsumer.accept(objects);
            });
}

// DELETE
public void deleteObject(Object object, Consumer<List<Object>> objectConsumer) {
    webClient.delete()
            .uri("endpoint_dir/" + object.getId())
            .retrieve()
            .toBodilessEntity()
            .onErrorStop()
            .subscribe(x -> {
                objects.remove(object);
                objectConsumer.accept(objects);
            });
}

// GET
public void getAllObjects(Consumer<List<Object>> objectConsumer, QueryParam...) {
    webClient.get()
            .uri(uriBuilder -> uriBuilder
                    .path("endpoint_dir")
                    .queryParam("paramName", paramValue) // can be repeated however many times needed
                    .build())
            .retrieve()
            .bodyToMono(new ParameterizedTypeReference<List<Object>>() {})
            .onErrorStop()
            .subscribe(newObjects -> {
                objects.clear();
                objects.addAll(newObjects);
                objectsConsumer.accept(objects);
            });
}
private final WebClient webClient; // web client to handle REST requests
private final List<Object> objects; // private, local list of objects to be stored

public ObjectController(){
    this.webClient = WebClient.builder()
            .baseUrl("http://localhost:8080/") // insert URL here
            .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .build();
    this.objects = new ArrayList<>();
}

// POST
public void addObject(Object object, Consumer<List<Object>> objectConsumer) {
    webClient.post()
            .uri("endpoint_dir")
            .bodyValue(object)
            .retrieve()
            .bodyToMono(Object.class)
            .onErrorStop()
            .subscribe(newObject -> {
                objects.add(newObject);
                objectConsumer.accept(objects);
            });
}

// PUT
public void updateObject(Object object, Consumer<List<Object>> objectConsumer) {
    webClient.put()
            .uri("endpoint_dir/" + object.getId())
            .bodyValue(object)
            .retrieve()
            .bodyToMono(Object.class)
            .onErrorStop()
            .subscribe(newObject -> {
                objects.replaceAll(oldObject -> oldObject.getId().equals(newObject.getId()) ? newObject : oldObject);
                objectConsumer.accept(objects);
            });
}

// DELETE
public void deleteObject(Object object, Consumer<List<Object>> objectConsumer) {
    webClient.delete()
            .uri("endpoint_dir/" + object.getId())
            .retrieve()
            .toBodilessEntity()
            .onErrorStop()
            .subscribe(x -> {
                objects.remove(object);
                objectConsumer.accept(objects);
            });
}

// GET
public void getAllObjects(Consumer<List<Object>> objectConsumer, QueryParam...) {
    webClient.get()
            .uri(uriBuilder -> uriBuilder
                    .path("endpoint_dir")
                    .queryParam("paramName", paramValue) // can be repeated however many times needed
                    .build())
            .retrieve()
            .bodyToMono(new ParameterizedTypeReference<List<Object>>() {})
            .onErrorStop()
            .subscribe(newObjects -> {
                objects.clear();
                objects.addAll(newObjects);
                objectsConsumer.accept(objects);
            });
}

Model-View-Controller

Multithreading

// via Runnable
public class MyRunnable implements Runnable{
    public int integer = 0;
    public void run(){
        System.out.println("Integer: " + integer++);
    }
}

public class Main{
    public static void main(String[] args){
        // with custom, explicit runnable
        Runnable r = new MyRunnable();
        Thread t = new Thread(r);
        
        t.start();
        t.join(); // surround with try...catch...
        
        // def run() method of runnable in lambda
        Thread t1 = new Thread(() -> {
            System.out.println("Lambda!");
        });
        t1.start();
    }
}
// via Runnable
public class MyRunnable implements Runnable{
    public int integer = 0;
    public void run(){
        System.out.println("Integer: " + integer++);
    }
}

public class Main{
    public static void main(String[] args){
        // with custom, explicit runnable
        Runnable r = new MyRunnable();
        Thread t = new Thread(r);
        
        t.start();
        t.join(); // surround with try...catch...
        
        // def run() method of runnable in lambda
        Thread t1 = new Thread(() -> {
            System.out.println("Lambda!");
        });
        t1.start();
    }
}

TEMPLATE: Using Locks

lockObject.lock();
try {
   // statements 
} 
finally {
   lockObject.unlock();
}
lockObject.lock();
try {
   // statements 
} 
finally {
   lockObject.unlock();
}

Semaphores

// buffer object
class Buffer {
    Semaphore free;
    Semaphore occupied;
}

// producer thread
produce(){
    free.acquire();
    synchronized(Buffer){
        ...
    }
    occupied.release();
}

// consumer thread
consume(){
    occupied.acquire();
    synchronized(Buffer){
        ...
    }
    free.release();
}
// buffer object
class Buffer {
    Semaphore free;
    Semaphore occupied;
}

// producer thread
produce(){
    free.acquire();
    synchronized(Buffer){
        ...
    }
    occupied.release();
}

// consumer thread
consume(){
    occupied.acquire();
    synchronized(Buffer){
        ...
    }
    free.release();
}

JUnit 5 and Mock Testing with EasyMock

JUnit Annotations and Assertions

TEMPLATE: EasyMock

@Mock // mock object
Object mockObject;

@TestSubject // SUT
Object testSubject = new Object();

@Test
public void test() {
    expect(mockObject.function()).andReturn(returnValue);
    replay(mockObject);
    // insert test as usual here
    verify(mockObject);
}
@Mock // mock object
Object mockObject;

@TestSubject // SUT
Object testSubject = new Object();

@Test
public void test() {
    expect(mockObject.function()).andReturn(returnValue);
    replay(mockObject);
    // insert test as usual here
    verify(mockObject);
}

SpotBugs, Infer and AddressSanitizer

Continuous Integration with Gradle

// add plugins
plugins {
    id 'sth.etc.pluginname.morethings' version 'version_number'
}

// add dependencies
// easy way: intellij bottom bar -> dependencies -> search and add -> reload
// manual labor:
dependencies {
    implementation 'ins.ert.coolname.here'
    testimplementation 'sth.to.do.withjunitprobably'
}

// directories
sourceSets {
    main {
        java {
            srcDir 'src'
        }
    }
    test {
        java {
            srcDir 'test'
        }
    }
}

// see project repositories for full gradle build files if necessary
// add plugins
plugins {
    id 'sth.etc.pluginname.morethings' version 'version_number'
}

// add dependencies
// easy way: intellij bottom bar -> dependencies -> search and add -> reload
// manual labor:
dependencies {
    implementation 'ins.ert.coolname.here'
    testimplementation 'sth.to.do.withjunitprobably'
}

// directories
sourceSets {
    main {
        java {
            srcDir 'src'
        }
    }
    test {
        java {
            srcDir 'test'
        }
    }
}

// see project repositories for full gradle build files if necessary

Refactoring

// example: L10H01
// subclasses: PhysicalTutorGroupMeeting and VirtualTutorGroupMeeting
public abstract class TutorGroupMeeting {
    // common attributes go here...

    public void practice(){                            // MOVED FUNCTION OF SUBCLASSES THAT GETS CALLED
        Student tutor = getTutorGroup().getTutor();
        greet(tutor);                                  // SUBCLASS-DEFINED
        session(tutor);                                // IMPLEMENTED HERE
        end(tutor);                                    // SUBCLASS-DEFINED
    }

    // common getters and setters go here...

    public abstract void greet(Student tutor);         // TO BE IMPLEMENTED IN SUBCLASS
    public void session(Student tutor){                // IMPLEMENTED HERE
        // identical code in both subclasses goes here...
    }
    public abstract void end(Student tutor);           // TO BE IMPLEMENTED IN SUBCLASS
}
// example: L10H01
// subclasses: PhysicalTutorGroupMeeting and VirtualTutorGroupMeeting
public abstract class TutorGroupMeeting {
    // common attributes go here...

    public void practice(){                            // MOVED FUNCTION OF SUBCLASSES THAT GETS CALLED
        Student tutor = getTutorGroup().getTutor();
        greet(tutor);                                  // SUBCLASS-DEFINED
        session(tutor);                                // IMPLEMENTED HERE
        end(tutor);                                    // SUBCLASS-DEFINED
    }

    // common getters and setters go here...

    public abstract void greet(Student tutor);         // TO BE IMPLEMENTED IN SUBCLASS
    public void session(Student tutor){                // IMPLEMENTED HERE
        // identical code in both subclasses goes here...
    }
    public abstract void end(Student tutor);           // TO BE IMPLEMENTED IN SUBCLASS
}

Merge Conflicts without Rebase


Summary by Flavius Schmidt, ge83pux, 2023.
https://home.in.tum.de/~scfl/