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}