From 04c13541739198a793ee143a2a3736e0bf96d182 Mon Sep 17 00:00:00 2001
From: Claudenir Fonseca <c.moraisfonseca@utwente.nl>
Date: Wed, 1 May 2024 02:00:57 +0200
Subject: [PATCH] [WIP] Add practical session guide

---
 meetings/practical-session-1.md               |  5 +-
 pom.xml                                       |  2 +-
 .../nl/utwente/mod4/pokemon/Listenner.java    | 35 +++++++
 .../java/nl/utwente/mod4/pokemon/PokeApp.java |  8 ++
 .../mod4/pokemon/PokemonTrainerDatabase.java  |  9 --
 .../java/nl/utwente/mod4/pokemon/Utils.java   | 10 ++
 .../mod4/pokemon/dao/PokemonTypeDao.java      | 95 +++++++++++++++++--
 .../mod4/pokemon/model/NamedEntity.java       | 12 ++-
 .../mod4/pokemon/model/PokemonType.java       |  8 +-
 .../pokemon/model/ResourceCollection.java     |  4 +
 .../mod4/pokemon/routes/PokemonTypeRoute.java | 44 +++++----
 src/main/webapp/WEB-INF/web.xml               | 12 ---
 src/main/webapp/index.html                    |  6 +-
 src/main/webapp/other.html                    | 11 ---
 14 files changed, 192 insertions(+), 69 deletions(-)
 create mode 100644 src/main/java/nl/utwente/mod4/pokemon/Listenner.java
 create mode 100644 src/main/java/nl/utwente/mod4/pokemon/PokeApp.java
 delete mode 100644 src/main/java/nl/utwente/mod4/pokemon/PokemonTrainerDatabase.java
 create mode 100644 src/main/java/nl/utwente/mod4/pokemon/Utils.java
 create mode 100644 src/main/java/nl/utwente/mod4/pokemon/model/ResourceCollection.java
 delete mode 100644 src/main/webapp/other.html

diff --git a/meetings/practical-session-1.md b/meetings/practical-session-1.md
index f9d373d..90b1591 100644
--- a/meetings/practical-session-1.md
+++ b/meetings/practical-session-1.md
@@ -71,9 +71,10 @@ The IntelliJ IDEA channel on YouTube provides a great step-by-step guide on how
 
 > For the purposes of this practical session, cloning this repository into your machine and opening its root directory as an IntelliJ project will already configure most things for deployment. Still, create a web application first, such that IntelliJ has already the path to your Tomcat and JDK installations.
 
-["Tomcat in IntelliJ IDEA Ultimate" by Dalia Abo Sheasha![](http://img.youtube.com/vi/ThBw3WBTw9Q/0.jpg)](http://www.youtube.com/watch?v=ThBw3WBTw9Q)
+[![](http://img.youtube.com/vi/ThBw3WBTw9Q/0.jpg)](http://www.youtube.com/watch?v=ThBw3WBTw9Q)
+["Tomcat in IntelliJ IDEA Ultimate" by Dalia Abo Sheasha](http://www.youtube.com/watch?v=ThBw3WBTw9Q)
 
-<!-- <iframe width="560" height="315" src="https://www.youtube.com/embed/ThBw3WBTw9Q?si=wB7XiVdEC7KFN8kV" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> -->
+<!-- ["Tomcat in IntelliJ IDEA Ultimate" by Dalia Abo Sheasha![](http://img.youtube.com/vi/ThBw3WBTw9Q/0.jpg)](http://www.youtube.com/watch?v=ThBw3WBTw9Q) -->
 
 After creating a project, please perform the following procedures:
 
diff --git a/pom.xml b/pom.xml
index b765370..423e5b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
     <groupId>nl.utwente.mod4</groupId>
     <artifactId>pokemon</artifactId>
     <version>1.0-SNAPSHOT</version>
-    <name>mod4-wp-2023-2024-pokemon</name>
+    <name>pokemon</name>
     <packaging>war</packaging>
 
     <properties>
diff --git a/src/main/java/nl/utwente/mod4/pokemon/Listenner.java b/src/main/java/nl/utwente/mod4/pokemon/Listenner.java
new file mode 100644
index 0000000..2581a77
--- /dev/null
+++ b/src/main/java/nl/utwente/mod4/pokemon/Listenner.java
@@ -0,0 +1,35 @@
+package nl.utwente.mod4.pokemon;
+
+import jakarta.servlet.ServletContextEvent;
+import jakarta.servlet.ServletContextListener;
+import jakarta.servlet.annotation.WebListener;
+import nl.utwente.mod4.pokemon.dao.PokemonTypeDao;
+
+import java.io.IOException;
+
+@WebListener
+public class Listenner implements ServletContextListener {
+    @Override
+    public void contextInitialized(ServletContextEvent sce) {
+        System.out.println("Initializing PokeApp...");
+        try {
+            PokemonTypeDao.INSTANCE.load();
+        } catch (IOException e) {
+            System.err.println("Error while loading data.");
+            e.printStackTrace();
+        }
+        System.out.println("PokeApp initialized.");
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce) {
+        System.out.println("Shutting down PokeApp...");
+        try {
+            PokemonTypeDao.INSTANCE.save();
+        } catch (IOException e) {
+            System.err.println("Error while saving data.");
+            e.printStackTrace();
+        }
+        System.out.println("PokeApp shutdown.");
+    }
+}
diff --git a/src/main/java/nl/utwente/mod4/pokemon/PokeApp.java b/src/main/java/nl/utwente/mod4/pokemon/PokeApp.java
new file mode 100644
index 0000000..715696b
--- /dev/null
+++ b/src/main/java/nl/utwente/mod4/pokemon/PokeApp.java
@@ -0,0 +1,8 @@
+package nl.utwente.mod4.pokemon;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+
+@ApplicationPath("/api")
+public class PokeApp extends Application {
+}
\ No newline at end of file
diff --git a/src/main/java/nl/utwente/mod4/pokemon/PokemonTrainerDatabase.java b/src/main/java/nl/utwente/mod4/pokemon/PokemonTrainerDatabase.java
deleted file mode 100644
index c806017..0000000
--- a/src/main/java/nl/utwente/mod4/pokemon/PokemonTrainerDatabase.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package nl.utwente.mod4.pokemon;
-
-import jakarta.servlet.http.HttpServlet;
-import jakarta.ws.rs.ApplicationPath;
-
-@ApplicationPath("/")
-public class PokemonTrainerDatabase extends HttpServlet {
-
-}
\ No newline at end of file
diff --git a/src/main/java/nl/utwente/mod4/pokemon/Utils.java b/src/main/java/nl/utwente/mod4/pokemon/Utils.java
new file mode 100644
index 0000000..1d2dce1
--- /dev/null
+++ b/src/main/java/nl/utwente/mod4/pokemon/Utils.java
@@ -0,0 +1,10 @@
+package nl.utwente.mod4.pokemon;
+
+import java.nio.file.Path;
+
+public class Utils {
+
+    public static String getAbsolutePathToResources() {
+        return Utils.class.getClassLoader().getResource("").getPath();
+    }
+}
diff --git a/src/main/java/nl/utwente/mod4/pokemon/dao/PokemonTypeDao.java b/src/main/java/nl/utwente/mod4/pokemon/dao/PokemonTypeDao.java
index 6448a47..119568f 100644
--- a/src/main/java/nl/utwente/mod4/pokemon/dao/PokemonTypeDao.java
+++ b/src/main/java/nl/utwente/mod4/pokemon/dao/PokemonTypeDao.java
@@ -1,20 +1,103 @@
 package nl.utwente.mod4.pokemon.dao;
 
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+import nl.utwente.mod4.pokemon.Utils;
 import nl.utwente.mod4.pokemon.model.PokemonType;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import java.time.Instant;
+import java.util.*;
 
-public class PokemonTypeDao {
+public enum PokemonTypeDao {
 
-    private static final String filePath = PokemonTypeDao.class.getClassLoader().getResource("/aggregated-and-filtered-pokemon-dataset.json").getPath();
+    INSTANCE;
 
-    public List<PokemonType> loadPokemonType() throws IOException {
+    private static final String ORIGINAL_POKEMON_TYPES = Utils.getAbsolutePathToResources() + "/aggregated-and-filtered-pokemon-dataset.json";
+    private static final String POKEMON_TYPES = Utils.getAbsolutePathToResources() + "/pokemon-types.json";
+
+    private HashMap<String, PokemonType> pokemonTypes = new HashMap<>();
+
+    public void delete(String id) {
+        if(pokemonTypes.containsKey(id)) {
+            pokemonTypes.remove(id);
+        } else {
+            throw new NotFoundException("Pokemon type '" + id + "' not found.");
+        }
+    }
+
+    public List<PokemonType> getPokemonTypes() {
+        return new ArrayList<>(pokemonTypes.values());
+    }
+
+    public PokemonType getPokemonType(String id) {
+        var pt = pokemonTypes.get(id);
+
+        if (pt == null) {
+            throw new NotFoundException("Pokemon '" + id + "' not found!");
+        }
+
+        return pt;
+    }
+
+    public void load() throws IOException {
         ObjectMapper mapper = new ObjectMapper();
-        return mapper.readValue(new File(filePath), new ArrayList<PokemonType>().getClass());
+        File source = existsPokemonTypes() ?
+                new File(POKEMON_TYPES) :
+                new File(ORIGINAL_POKEMON_TYPES);
+        PokemonType[] arr = mapper.readValue(source, PokemonType[].class);
+
+        Arrays.stream(arr).forEach(pt -> pokemonTypes.put(pt.id, pt));
     }
 
+    public void save() throws IOException {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectWriter writer = mapper.writer(new DefaultPrettyPrinter());
+        File destination = new File(POKEMON_TYPES);
+
+        writer.writeValue(destination, pokemonTypes.values());
+    }
+
+    private boolean existsPokemonTypes() {
+        File f = new File(POKEMON_TYPES);
+        return f.exists() && !f.isDirectory();
+    }
+
+    private int getMaxId() {
+        Set<String> ids = pokemonTypes.keySet();
+        return ids.isEmpty() ? 0 : ids.stream()
+                .map(Integer::parseInt)
+                .max(Integer::compareTo)
+                .get();
+    }
+
+
+    public PokemonType create(PokemonType newPokemonType) {
+        String nextId = "" + (getMaxId() + 1);
+
+        newPokemonType.id = nextId;
+        newPokemonType.created = Instant.now().toString();
+        newPokemonType.lastUpDate = Instant.now().toString();
+        pokemonTypes.put(nextId,newPokemonType);
+
+        return newPokemonType;
+    }
+
+    public PokemonType update(PokemonType updated) {
+        if(!updated.isValid())
+            throw new BadRequestException("Invalid pokemon type.");
+        if(pokemonTypes.get(updated.id) == null)
+            throw new NotFoundException("Pokemon type id '" + updated.id + "' not found.");
+
+        updated.lastUpDate = Instant.now().toString();
+        pokemonTypes.put(updated.id,updated);
+
+        return updated;
+    }
 }
diff --git a/src/main/java/nl/utwente/mod4/pokemon/model/NamedEntity.java b/src/main/java/nl/utwente/mod4/pokemon/model/NamedEntity.java
index 0eee4ca..71acb32 100644
--- a/src/main/java/nl/utwente/mod4/pokemon/model/NamedEntity.java
+++ b/src/main/java/nl/utwente/mod4/pokemon/model/NamedEntity.java
@@ -10,10 +10,14 @@ public class NamedEntity {
     public String lastUpDate;
 
     public NamedEntity() {
-        id = "0";
-        name = "Missingno";
-        created = Instant.now().toString();
-        lastUpDate = Instant.now().toString();
+        id = null;
+        name = null;
+        created = null;
+        lastUpDate = null;
+    }
+
+    public boolean isValid() {
+        return id != null && !id.isEmpty();
     }
 
 }
diff --git a/src/main/java/nl/utwente/mod4/pokemon/model/PokemonType.java b/src/main/java/nl/utwente/mod4/pokemon/model/PokemonType.java
index bf112af..442aea6 100644
--- a/src/main/java/nl/utwente/mod4/pokemon/model/PokemonType.java
+++ b/src/main/java/nl/utwente/mod4/pokemon/model/PokemonType.java
@@ -28,9 +28,9 @@ public class PokemonType extends NamedEntity {
         super();
         pokedexNumber = 0;
         generation = 0;
-        japaneseName = "";
-        classification = "";
-        abilities = new ArrayList<>();
+        japaneseName = null;
+        classification = null;
+        abilities = null;
         baseHeight = 0;
         baseWeight = 0;
         baseHp = 0;
@@ -41,7 +41,7 @@ public class PokemonType extends NamedEntity {
         baseSpeed = 0;
         captureRate = 0;
         isLegendary = false;
-        imgUrl = "";
+        imgUrl = null;
     }
 
 }
diff --git a/src/main/java/nl/utwente/mod4/pokemon/model/ResourceCollection.java b/src/main/java/nl/utwente/mod4/pokemon/model/ResourceCollection.java
new file mode 100644
index 0000000..fb2868e
--- /dev/null
+++ b/src/main/java/nl/utwente/mod4/pokemon/model/ResourceCollection.java
@@ -0,0 +1,4 @@
+package nl.utwente.mod4.pokemon.model;
+
+public class ResourceCollection {
+}
diff --git a/src/main/java/nl/utwente/mod4/pokemon/routes/PokemonTypeRoute.java b/src/main/java/nl/utwente/mod4/pokemon/routes/PokemonTypeRoute.java
index 6992762..298706d 100644
--- a/src/main/java/nl/utwente/mod4/pokemon/routes/PokemonTypeRoute.java
+++ b/src/main/java/nl/utwente/mod4/pokemon/routes/PokemonTypeRoute.java
@@ -6,37 +6,45 @@ import jakarta.ws.rs.core.Response;
 import nl.utwente.mod4.pokemon.dao.PokemonTypeDao;
 import nl.utwente.mod4.pokemon.model.PokemonType;
 
-import java.io.IOException;
 import java.util.List;
 
 @Path("/pokemonTypes")
 public class PokemonTypeRoute {
 
     @GET
-    @Path("/")
     @Produces(MediaType.APPLICATION_JSON)
     public List<PokemonType> getPokemon() {
-        try {
-            return new PokemonTypeDao().loadPokemonType();
-        } catch (IOException e) {
-            throw new ServerErrorException(Response.status(500).build());
-        }
+        return PokemonTypeDao.INSTANCE.getPokemonTypes();
     }
 
-//    @POST
-//    @Path("/")
-//    @Produces(MediaType.APPLICATION_JSON)
-//    @Consumes(MediaType.APPLICATION_JSON)
-//    public String createPokemon() {
-//        return "Returning all pokemon types.";
-//    }
+    @POST
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public PokemonType createPokemon(PokemonType pokemonType) {
+        return PokemonTypeDao.INSTANCE.create(pokemonType);
+    }
 
     @GET
     @Path("/{id}")
     @Produces(MediaType.APPLICATION_JSON)
-    public nl.utwente.mod4.pokemon.model.PokemonType getPokemon(@PathParam("id") String id) {
-        var pokemon = new nl.utwente.mod4.pokemon.model.PokemonType();
-        pokemon.id = id;
-        return pokemon;
+    public PokemonType getPokemon(@PathParam("id") String id) {
+        return PokemonTypeDao.INSTANCE.getPokemonType(id);
+    }
+
+    @PUT
+    @Path("/{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public PokemonType updatePokemon(@PathParam("id") String id, PokemonType toUpdate) {
+        if(id == null || !id.equals(toUpdate.id))
+            throw new BadRequestException("Id mismatch.");
+
+        return PokemonTypeDao.INSTANCE.update(toUpdate);
+    }
+
+    @DELETE
+    @Path("/{id}")
+    public void deletePokemon(@PathParam("id") String id) {
+        PokemonTypeDao.INSTANCE.delete(id);
     }
 }
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index 9a3a4db..e152fae 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -8,16 +8,4 @@
     <welcome-file-list>
         <welcome-file>index.html</welcome-file>
     </welcome-file-list>
-
-    <servlet>
-        <description>Pokemon Servlet</description>
-        <display-name>PokemonTrainerDatabase</display-name>
-        <servlet-name>PokemonTrainerDatabase</servlet-name>
-        <servlet-class>nl.utwente.mod4.pokemon.PokemonTrainerDatabase</servlet-class>
-    </servlet>
-
-    <servlet-mapping>
-        <servlet-name>PokemonTrainerDatabase</servlet-name>
-        <url-pattern>/pokemon</url-pattern>
-    </servlet-mapping>
 </web-app>
\ No newline at end of file
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
index 21f9156..297e572 100644
--- a/src/main/webapp/index.html
+++ b/src/main/webapp/index.html
@@ -6,6 +6,8 @@
 </head>
 <body>
 <p>Hello there</p>
-<img src="/images/abra.png"/>
-</body>
+<img src="/pokemon/images/abra.png"/>
+<form action="/pokemon/api/pokemonTypes">
+    <input type="submit" value="Pokemon Types" />
+</form></body>
 </html>
\ No newline at end of file
diff --git a/src/main/webapp/other.html b/src/main/webapp/other.html
deleted file mode 100644
index 76bb1d7..0000000
--- a/src/main/webapp/other.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>Title</title>
-</head>
-<body>
-<p>Hello there</p>
-<img src="images/abra.png"/>
-</body>
-</html>
\ No newline at end of file
-- 
GitLab