001package edu.pdx.cs410J.servlets; 002 003import edu.pdx.cs410J.rmi.Movie; 004import edu.pdx.cs410J.rmi.MovieDatabase; 005import edu.pdx.cs410J.rmi.MovieDatabaseImpl; 006 007import javax.servlet.ServletException; 008import javax.servlet.http.HttpServlet; 009import javax.servlet.http.HttpServletRequest; 010import javax.servlet.http.HttpServletResponse; 011import java.io.BufferedReader; 012import java.io.IOException; 013import java.io.PrintWriter; 014import java.rmi.RemoteException; 015import java.util.Comparator; 016import java.util.HashMap; 017import java.util.Map; 018 019import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; 020import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; 021 022/** 023 * A servlet that provides REST access to a movie database 024 */ 025public class MovieDatabaseServlet extends HttpServlet { 026 private MovieDatabase database; 027 028 enum DataType { 029 MOVIE, ACTOR, CHARACTER, UNKNOWN 030 } 031 032 @Override 033 public void init() throws ServletException { 034 this.database = new MovieDatabaseImpl(); 035 } 036 037 /** 038 * Returns the type of data requested 039 */ 040 private DataType getDataType(HttpServletRequest request) { 041 String uri = request.getRequestURI(); 042 if (uri.contains("actors")) { 043 return DataType.ACTOR; 044 045 } else if (uri.contains("characters")) { 046 return DataType.CHARACTER; 047 048 } else if (uri.contains("movies")) { 049 return DataType.MOVIE; 050 051 } else { 052 return DataType.UNKNOWN; 053 } 054 } 055 056 /** 057 * Updates information in the database 058 * 059 * @param request The request from the client. Note that unlike POST, data 060 * arrives in the body (conent) of the request. {#link getParameter} returns 061 * <code>null</code> 062 */ 063 @Override 064 protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 065 Map<String, String> parameters = readParametersFromRequest(request); 066 067 response.setContentType("text/plain"); 068 DataType dataType = getDataType(request); 069 switch (dataType) { 070 case MOVIE: 071 updateMovie(parameters, response); 072 break; 073 case UNKNOWN: 074 response.sendError(SC_BAD_REQUEST, "Unknown dataType: " + dataType); 075 break; 076 } 077 } 078 079 private Map<String, String> readParametersFromRequest(HttpServletRequest request) throws IOException { 080 Map<String, String> parameters = new HashMap<>(); 081 BufferedReader br = request.getReader(); // new BufferedReader(new InputStreamReader(request.getInputStream(), Charset.forName("UTF-8"))); 082 for (String line = br.readLine(); line != null; line = br.readLine()) { 083 String[] strings = line.split("="); 084 parameters.put(strings[0], strings[1]); 085 } 086 return parameters; 087 } 088 089 /** 090 * Creates a new movie in the database 091 */ 092 private void createMovie(HttpServletRequest parameters, HttpServletResponse response) throws IOException { 093 String title = parameters.getParameter("title"); 094 if (notExists(title)) { 095 response.sendError(SC_BAD_REQUEST, "Missing title"); 096 return; 097 } 098 099 String yearString = parameters.getParameter("year"); 100 if (notExists(yearString)) { 101 response.sendError(SC_BAD_REQUEST, "Missing year"); 102 return; 103 } 104 105 int year; 106 try { 107 year = Integer.parseInt(yearString); 108 109 } catch (NumberFormatException ex) { 110 response.sendError(SC_BAD_REQUEST, "Malformed year: " + yearString); 111 return; 112 } 113 114 long movieId = this.database.createMovie(title, year); 115 PrintWriter pw = response.getWriter(); 116 pw.println(movieId); 117 } 118 119 /** 120 * Returns whether or not the given parameter value was specified 121 * @return <code>true</code> if <code>value</code> is <code>null</code> or 122 * the empty string 123 */ 124 private boolean notExists(String value) { 125 return value == null || value.equals(""); 126 } 127 128 /** 129 * Searches for information in the database 130 */ 131 @Override 132 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 133 response.setContentType("text/plain"); 134 135 switch (getDataType(request)) { 136 case MOVIE: 137 dumpMovies(request, response); 138 } 139 } 140 141 /** 142 * Creates information in the database 143 */ 144 @Override 145 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 146 response.setContentType("text/plain"); 147 148 switch (getDataType(request)) { 149 case MOVIE: 150 createMovie(request, response); 151 } 152 } 153 154 private void updateMovie(Map<String, String> request, HttpServletResponse response) throws IOException { 155// System.out.println(request.getParameterMap().size() + " parameters"); 156// for (Object key : request.getParameterMap().keySet()) { 157// String value = request.getParameter((String) key); 158// System.out.println(" " + key + " -> " + value); 159// } 160 161 String idString = request.get("id"); 162 if (notExists(idString)) { 163 response.sendError(SC_BAD_REQUEST, "Missing id"); 164 return; 165 } 166 167 Movie movie; 168 try { 169 movie = this.database.getMovie(Long.parseLong(idString)); 170 171 } catch (NumberFormatException ex) { 172 response.sendError(SC_BAD_REQUEST, "Malformed id: " + idString); 173 return; 174 } 175 176 if (movie == null) { 177 response.sendError(SC_BAD_REQUEST, "Unknown movie: " + idString); 178 return; 179 } 180 181 String title = request.get("title"); 182 if (title != null) { 183 movie.setTitle(title); 184 } 185 186 String yearString = request.get("year"); 187 if (yearString != null) { 188 try { 189 movie.setYear(Integer.parseInt(yearString)); 190 response.getWriter().println(idString); 191 192 } catch (NumberFormatException ex) { 193 response.sendError(SC_BAD_REQUEST, "Malformed year: " + yearString); 194 } 195 } 196 } 197 198 /** 199 * Writes a description of one or more movies back to the client 200 */ 201 private void dumpMovies(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 202 Comparator<Movie> sorter = (m1, m2) -> { 203 long id1 = m1.getId(); 204 long id2 = m2.getId(); 205 return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; 206 }; 207 208 String idString = request.getParameter("id"); 209 try { 210 if (notExists(idString)) { 211 PrintWriter pw = response.getWriter(); 212 final String title = request.getParameter("title"); 213 final String year = request.getParameter("year"); 214 215 if (notExists(title) && notExists(year)) { 216 // Dump all movies 217 for (Movie movie : this.database.getMovies()) { 218 dumpMovie(movie, pw); 219 } 220 221 } else { 222 for (Movie movie: this.database.executeQuery(movie1 -> { 223 if (notExists(title)) { 224 return movie1.getYear() == Integer.parseInt(year); 225 226 } else if (notExists(year)) { 227 return movie1.getTitle().contains(title); 228 229 } else { 230 return movie1.getYear() == Integer.parseInt(year) && movie1.getTitle().contains(title); 231 } 232 }, sorter)) { 233 dumpMovie(movie, pw); 234 } 235 } 236 237 238 } else { 239 try { 240 Movie movie = this.database.getMovie(Long.parseLong(idString)); 241 if (movie == null) { 242 response.sendError(SC_NOT_FOUND, "There is no movie with id " + idString); 243 244 } else { 245 dumpMovie(movie, response.getWriter()); 246 } 247 } catch (NumberFormatException ex) { 248 response.sendError(SC_BAD_REQUEST, "Invalid movie id: " + idString); 249 } 250 } 251 } catch (RemoteException ex) { 252 throw new ServletException(ex); 253 } 254 } 255 256 /** 257 * Writes a description of a movie back to the client 258 */ 259 private void dumpMovie(Movie movie, PrintWriter pw) { 260 pw.println(movie.getId() + " \"" + movie.getTitle() + "\" (" + movie.getYear() + ")"); 261 } 262 263 /** 264 * Removes information from the database 265 */ 266 @Override 267 protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 268 Map<String, String> parameters = readParametersFromRequest(request); 269 270 response.setContentType("text/plain"); 271 DataType dataType = getDataType(request); 272 switch (dataType) { 273 case MOVIE: 274 deleteMovie(parameters, response); 275 break; 276 case UNKNOWN: 277 response.sendError(SC_BAD_REQUEST, "Unknown dataType: " + dataType); 278 break; 279 } 280 } 281 282 private void deleteMovie(Map<String, String> request, HttpServletResponse response) throws IOException { 283 String idString = request.get("id"); 284 if (notExists(idString)) { 285 response.sendError(SC_BAD_REQUEST, "Missing id"); 286 return; 287 } 288 289 Movie movie; 290 try { 291 movie = this.database.getMovie(Long.parseLong(idString)); 292 293 } catch (NumberFormatException ex) { 294 response.sendError(SC_BAD_REQUEST, "Malformed id: " + idString); 295 return; 296 } 297 298 if (movie == null) { 299 response.sendError(SC_NOT_FOUND, "Unknown movie: " + idString); 300 return; 301 } 302 303 this.database.deleteMovie(movie.getId()); 304 } 305}