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