diff --git a/meetings/practical-session-1.md b/meetings/practical-session-1.md index f9d373dd25a01b4548e886822e9cb1db09cac4f5..90b1591890738c6c4a84d32a5cf338893b03edfe 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://www.youtube.com/watch?v=ThBw3WBTw9Q) +[](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://www.youtube.com/watch?v=ThBw3WBTw9Q) --> After creating a project, please perform the following procedures: diff --git a/pom.xml b/pom.xml index b765370727f6a88630ab15fc433b879bdc0b9f51..423e5b458041309b844b85e789ece0453306b0eb 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 0000000000000000000000000000000000000000..2581a77196345e1a13b31a4f5b9051250638b5dc --- /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 0000000000000000000000000000000000000000..715696bd68b8ec6b2a3250e8e7acd11676d56af8 --- /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 c80601735b9d16808015bb1e07259cb79b9f5b7f..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..1d2dce1e568186606c796b8af6f264dec966efd5 --- /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 6448a470531548c19ff6b5e5e1186bf54268394f..119568f3a2401dc07400011221332649ccb7931b 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 0eee4ca32269232cfbb993a9301d8436bc4446fa..71acb3215b7870d46815f95beb919b213011f548 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 bf112afb80a0c4148c03cb73d8845b7866188e65..442aea668daa1401026473d6531e3b11bcf97a59 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 0000000000000000000000000000000000000000..fb2868ea4538fc84ce801203be3356e3520877f7 --- /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 69927626fd63b63aba6a60852fced3ae61d4d539..298706dce80154e7fd463969794094931c123549 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 9a3a4db24219f75d6ac90305fd8bf0ce3f924bd0..e152fae147cc09404cc11f911c96f5021db0f6ec 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 21f91561d5365c5fd9102499a444728c103f8f43..297e57248edfaef7113311f75ee797268a8e56c5 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 76bb1d7ce581151061125edeb04115db779ce0ab..0000000000000000000000000000000000000000 --- 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