001package edu.pdx.cs.joy.phonebillweb; 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>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 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}