001package edu.pdx.cs410J.phonebillweb;
002
003import com.google.common.annotations.VisibleForTesting;
004
005import javax.servlet.http.HttpServlet;
006import javax.servlet.http.HttpServletRequest;
007import javax.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>PhoneBill</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 PhoneBillServlet 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            writeDefinition(word, response);
040
041        } else {
042            writeAllDictionaryEntries(response);
043        }
044    }
045
046    /**
047     * Handles an HTTP POST request by storing the dictionary entry for the
048     * "word" and "definition" request parameters.  It writes the dictionary
049     * entry to the HTTP response.
050     */
051    @Override
052    protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws IOException
053    {
054        response.setContentType( "text/plain" );
055
056        String word = getParameter(WORD_PARAMETER, request );
057        if (word == null) {
058            missingRequiredParameter(response, WORD_PARAMETER);
059            return;
060        }
061
062        String definition = getParameter(DEFINITION_PARAMETER, request );
063        if ( definition == null) {
064            missingRequiredParameter( response, DEFINITION_PARAMETER );
065            return;
066        }
067
068        this.dictionary.put(word, definition);
069
070        PrintWriter pw = response.getWriter();
071        pw.println(Messages.definedWordAs(word, definition));
072        pw.flush();
073
074        response.setStatus( HttpServletResponse.SC_OK);
075    }
076
077    /**
078     * Handles an HTTP DELETE request by removing all dictionary entries.  This
079     * behavior is exposed for testing purposes only.  It's probably not
080     * something that you'd want a real application to expose.
081     */
082    @Override
083    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException {
084        response.setContentType("text/plain");
085
086        this.dictionary.clear();
087
088        PrintWriter pw = response.getWriter();
089        pw.println(Messages.allDictionaryEntriesDeleted());
090        pw.flush();
091
092        response.setStatus(HttpServletResponse.SC_OK);
093
094    }
095
096    /**
097     * Writes an error message about a missing parameter to the HTTP response.
098     *
099     * The text of the error message is created by {@link Messages#missingRequiredParameter(String)}
100     */
101    private void missingRequiredParameter( HttpServletResponse response, String parameterName )
102        throws IOException
103    {
104        String message = Messages.missingRequiredParameter(parameterName);
105        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED, message);
106    }
107
108    /**
109     * Writes the definition of the given word to the HTTP response.
110     *
111     * The text of the message is formatted with {@link TextDumper}
112     */
113    private void writeDefinition(String word, HttpServletResponse response) throws IOException {
114        String definition = this.dictionary.get(word);
115
116        if (definition == null) {
117            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
118
119        } else {
120            PrintWriter pw = response.getWriter();
121
122            Map<String, String> wordDefinition = Map.of(word, definition);
123            TextDumper dumper = new TextDumper(pw);
124            dumper.dump(wordDefinition);
125
126            response.setStatus(HttpServletResponse.SC_OK);
127        }
128    }
129
130    /**
131     * Writes all of the dictionary entries to the HTTP response.
132     *
133     * The text of the message is formatted with {@link TextDumper}
134     */
135    private void writeAllDictionaryEntries(HttpServletResponse response ) throws IOException
136    {
137        PrintWriter pw = response.getWriter();
138        TextDumper dumper = new TextDumper(pw);
139        dumper.dump(dictionary);
140
141        response.setStatus( HttpServletResponse.SC_OK );
142    }
143
144    /**
145     * Returns the value of the HTTP request parameter with the given name.
146     *
147     * @return <code>null</code> if the value of the parameter is
148     *         <code>null</code> or is the empty string
149     */
150    private String getParameter(String name, HttpServletRequest request) {
151      String value = request.getParameter(name);
152      if (value == null || "".equals(value)) {
153        return null;
154
155      } else {
156        return value;
157      }
158    }
159
160    @VisibleForTesting
161    String getDefinition(String word) {
162        return this.dictionary.get(word);
163    }
164
165}