001package edu.pdx.cs.joy.airlineweb;
002
003import com.google.common.annotations.VisibleForTesting;
004
005import jakarta.servlet.http.HttpServlet;
006import jakarta.servlet.http.HttpServletRequest;
007import jakarta.servlet.http.HttpServletResponse;
008import java.io.IOException;
009import java.io.PrintWriter;
010import java.util.HashMap;
011import java.util.Map;
012
013/**
014 * This servlet ultimately provides a REST API for working with an
015 * <code>Airline</code>.  However, in its current state, it is an example
016 * of how to use HTTP and Java servlets to store simple dictionary of words
017 * and their definitions.
018 */
019public class AirlineServlet extends HttpServlet {
020  static final String WORD_PARAMETER = "word";
021  static final String DEFINITION_PARAMETER = "definition";
022
023  private final Map<String, String> dictionary = new HashMap<>();
024
025  /**
026   * Handles an HTTP GET request from a client by writing the definition of the
027   * word specified in the "word" HTTP parameter to the HTTP response.  If the
028   * "word" parameter is not specified, all of the entries in the dictionary
029   * are written to the HTTP response.
030   */
031  @Override
032  protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws IOException
033  {
034      response.setContentType( "text/plain" );
035
036      String word = getParameter( WORD_PARAMETER, request );
037      if (word != null) {
038          log("GET " + word);
039          writeDefinition(word, response);
040
041      } else {
042          log("GET all dictionary entries");
043          writeAllDictionaryEntries(response);
044      }
045  }
046
047  /**
048   * Handles an HTTP POST request by storing the dictionary entry for the
049   * "word" and "definition" request parameters.  It writes the dictionary
050   * entry to the HTTP response.
051   */
052  @Override
053  protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws IOException
054  {
055      response.setContentType( "text/plain" );
056
057      String word = getParameter(WORD_PARAMETER, request );
058      if (word == null) {
059          missingRequiredParameter(response, WORD_PARAMETER);
060          return;
061      }
062
063      String definition = getParameter(DEFINITION_PARAMETER, request );
064      if ( definition == null) {
065          missingRequiredParameter( response, DEFINITION_PARAMETER );
066          return;
067      }
068
069      log("POST " + word + " -> " + definition);
070
071      this.dictionary.put(word, definition);
072
073      PrintWriter pw = response.getWriter();
074      pw.println(Messages.definedWordAs(word, definition));
075      pw.flush();
076
077      response.setStatus( HttpServletResponse.SC_OK);
078  }
079
080  /**
081   * Handles an HTTP DELETE request by removing all dictionary entries.  This
082   * behavior is exposed for testing purposes only.  It's probably not
083   * something that you'd want a real application to expose.
084   */
085  @Override
086  protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException {
087      response.setContentType("text/plain");
088
089      log("DELETE all dictionary entries");
090
091      this.dictionary.clear();
092
093      PrintWriter pw = response.getWriter();
094      pw.println(Messages.allDictionaryEntriesDeleted());
095      pw.flush();
096
097      response.setStatus(HttpServletResponse.SC_OK);
098
099  }
100
101  /**
102   * Writes an error message about a missing parameter to the HTTP response.
103   *
104   * The text of the error message is created by {@link Messages#missingRequiredParameter(String)}
105   */
106  private void missingRequiredParameter( HttpServletResponse response, String parameterName )
107      throws IOException
108  {
109      String message = Messages.missingRequiredParameter(parameterName);
110      response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED, message);
111  }
112
113  /**
114   * Writes the definition of the given word to the HTTP response.
115   *
116   * The text of the message is formatted with {@link TextDumper}
117   */
118  private void writeDefinition(String word, HttpServletResponse response) throws IOException {
119    String definition = this.dictionary.get(word);
120
121    if (definition == null) {
122      response.setStatus(HttpServletResponse.SC_NOT_FOUND);
123
124    } else {
125      PrintWriter pw = response.getWriter();
126
127      Map<String, String> wordDefinition = Map.of(word, definition);
128      TextDumper dumper = new TextDumper(pw);
129      dumper.dump(wordDefinition);
130
131      response.setStatus(HttpServletResponse.SC_OK);
132    }
133  }
134
135  /**
136   * Writes all of the dictionary entries to the HTTP response.
137   *
138   * The text of the message is formatted with {@link TextDumper}
139   */
140  private void writeAllDictionaryEntries(HttpServletResponse response ) throws IOException
141  {
142      PrintWriter pw = response.getWriter();
143      TextDumper dumper = new TextDumper(pw);
144      dumper.dump(dictionary);
145
146      response.setStatus( HttpServletResponse.SC_OK );
147  }
148
149  /**
150   * Returns the value of the HTTP request parameter with the given name.
151   *
152   * @return <code>null</code> if the value of the parameter is
153   *         <code>null</code> or is the empty string
154   */
155  private String getParameter(String name, HttpServletRequest request) {
156    String value = request.getParameter(name);
157    if (value == null || "".equals(value)) {
158      return null;
159
160    } else {
161      return value;
162    }
163  }
164
165  @VisibleForTesting
166  String getDefinition(String word) {
167      return this.dictionary.get(word);
168  }
169
170  @Override
171  public void log(String msg) {
172    System.out.println(msg);
173  }
174}