do not return error messages, only return the error code and let the client look up the error message. this could eventually allow for localization of the error messages (but not the rest of the client yet)

This commit is contained in:
Andy Janata 2012-01-16 14:33:58 -08:00
parent 9a281cd38f
commit 61451105b0
11 changed files with 103 additions and 38 deletions

View File

@ -16,7 +16,7 @@ cah.ajax.SuccessHandlers.register = function(data) {
};
cah.ajax.ErrorHandlers.register = function(data) {
$("#nickbox_error").text(data.error_message);
$("#nickbox_error").text(cah.$.ErrorCode_msg[data.error_code]);
$("#nickname").focus();
};

View File

@ -75,7 +75,7 @@ cah.Ajax.prototype.done = function(data) {
if (req && cah.ajax.ErrorHandlers[req.op]) {
cah.ajax.ErrorHandlers[req.op](data);
} else {
cah.log.error(data.error_message);
cah.log.error(cah.$.ErrorCode_msg[data.error_code]);
}
} else {
var req = this.pendingRequests[data.serial];

View File

@ -26,7 +26,6 @@ cah.$.AjaxResponse.NEXT = "next";
cah.$.AjaxResponse.ERROR = "error";
cah.$.AjaxResponse.ERROR_CODE = "error_code";
cah.$.AjaxResponse.SERIAL = "serial";
cah.$.AjaxResponse.ERROR_MESSAGE = "error_message";
cah.$.AjaxResponse.IN_PROGRESS = "in_progress";
cah.$.AjaxResponse.NICKNAME = "nickname";
cah.$.AjaxResponse.NAMES = "names";
@ -45,10 +44,27 @@ cah.$.ErrorCode = function() {
cah.$.ErrorCode.prototype.dummy = undefined;
cah.$.ErrorCode.NO_SESSION = "no_session";
cah.$.ErrorCode.NOT_REGISTERED = "not_registered";
cah.$.ErrorCode.INVALID_NICK = "invalid_nick";
cah.$.ErrorCode.BAD_REQUEST = "bad_req";
cah.$.ErrorCode.OP_NOT_SPECIFIED = "op_not_spec";
cah.$.ErrorCode.NO_MSG_SPECIFIED = "no_msg_spec";
cah.$.ErrorCode.NICK_IN_USE = "nick_in_use";
cah.$.ErrorCode.SESSION_EXPIRED = "session_expired";
cah.$.ErrorCode.MESSAGE_TOO_LONG = "msg_too_long";
cah.$.ErrorCode.NO_NICK_SPECIFIED = "no_nick_spec";
cah.$.ErrorCode.BAD_OP = "bad_op";
cah.$.ErrorCode_msg = {};
cah.$.ErrorCode_msg['nick_in_use'] = "Nickname is already in use.";
cah.$.ErrorCode_msg['bad_op'] = "Invalid operation.";
cah.$.ErrorCode_msg['not_registered'] = "Not registered. Refresh the page.";
cah.$.ErrorCode_msg['bad_req'] = "Bad request.";
cah.$.ErrorCode_msg['msg_too_long'] = "Messages cannot be longer than 200 characters.";
cah.$.ErrorCode_msg['session_expired'] = "Your session has expired. Refresh the page.";
cah.$.ErrorCode_msg['op_not_spec'] = "Operation not specified.";
cah.$.ErrorCode_msg['no_msg_spec'] = "No message specified.";
cah.$.ErrorCode_msg['invalid_nick'] = "Nickname must contain only upper and lower case letters, numbers, or underscores, must be 3 to 30 characters long, and must not start with a number.";
cah.$.ErrorCode_msg['no_session'] = "Session not detected. Make sure you have cookies enabled.";
cah.$.ErrorCode_msg['no_nick_spec'] = "No nickname specified.";
cah.$.LongPollEvent = function() {
// pass

View File

@ -51,7 +51,7 @@ cah.longpoll.done = function(data_list) {
if (cah.longpoll.ErrorCodeHandlers[data.error_code]) {
cah.longpoll.ErrorCodeHandlers[data.error_code](data);
} else {
cah.log.error(data.error_message);
cah.log.error(cah.$.ErrorCode_msg[data.error_code]);
}
} else {
if (cah.longpoll.EventHandlers[data.event]) {

View File

@ -40,7 +40,7 @@ public class AjaxServlet extends CahServlet {
try {
serial = Integer.parseInt(request.getParameter("serial"));
} catch (final NumberFormatException nfe) {
returnError(out, ErrorCode.BAD_REQUEST, "Bad request");
returnError(out, ErrorCode.BAD_REQUEST);
return;
}
}
@ -48,7 +48,7 @@ public class AjaxServlet extends CahServlet {
final String op = request.getParameter("op");
// !Handlers.LIST.containsKey(op)
if (op == null || op.equals("")) {
returnError(out, ErrorCode.OP_NOT_SPECIFIED, "Operation not specified.", serial);
returnError(out, ErrorCode.OP_NOT_SPECIFIED, serial);
return;
}
@ -56,7 +56,7 @@ public class AjaxServlet extends CahServlet {
try {
handler = getInjector().getInstance(Handlers.LIST.get(op));
} catch (final Exception e) {
returnError(out, ErrorCode.BAD_OP, "Invalid operation.", serial);
returnError(out, ErrorCode.BAD_OP, serial);
return;
}
final Map<ReturnableData, Object> data = handler.handle(request.getParameterMap(), hSession);

View File

@ -54,17 +54,14 @@ public abstract class CahServlet extends HttpServlet {
if (hSession.isNew()) {
// they should have gotten a session from the index page.
// they probably don't have cookies on.
returnError(response.getWriter(), ErrorCode.NO_SESSION,
"Session not detected. Make sure you have cookies enabled.");
returnError(response.getWriter(), ErrorCode.NO_SESSION);
} else if (!skipSessionUserCheck && hSession.getAttribute("user") == null) {
returnError(response.getWriter(), ErrorCode.NOT_REGISTERED,
"Not registered. Refresh the page.");
returnError(response.getWriter(), ErrorCode.NOT_REGISTERED);
} else if (hSession.getAttribute("user") != null
&& !(((User) hSession.getAttribute("user")).isValid())) {
// user probably pinged out
hSession.invalidate();
returnError(response.getWriter(), ErrorCode.SESSION_EXPIRED,
"Your session has expired. Refresh the page.");
returnError(response.getWriter(), ErrorCode.SESSION_EXPIRED);
} else {
handleRequest(request, response, hSession);
}
@ -90,27 +87,22 @@ public abstract class CahServlet extends HttpServlet {
* @param writer
* @param code
* Error code that the js code knows how to handle.
* @param message
* User-visible error message.
*/
protected void returnError(final PrintWriter writer, final ErrorCode code, final String message) {
returnError(writer, code, message, -1);
protected void returnError(final PrintWriter writer, final ErrorCode code) {
returnError(writer, code, -1);
}
/**
* Return an error to the client.
*
* @param writer
* @param message
* @param serial
*/
@SuppressWarnings("unchecked")
protected void returnError(final PrintWriter writer, final ErrorCode code, final String message,
final int serial) {
protected void returnError(final PrintWriter writer, final ErrorCode code, final int serial) {
final JSONObject ret = new JSONObject();
ret.put(AjaxResponse.ERROR, Boolean.TRUE);
ret.put(AjaxResponse.ERROR_CODE, code.toString());
ret.put(AjaxResponse.ERROR_MESSAGE, message);
writer.println(ret.toJSONString());
}

View File

@ -4,6 +4,10 @@ public class Constants {
public interface ReturnableData {
}
public interface Localizable {
public String getString();
}
public enum DisconnectReason {
KICKED("kicked"),
MANUAL("manual"),
@ -58,7 +62,6 @@ public class Constants {
public enum AjaxResponse implements ReturnableData {
ERROR("error"),
ERROR_CODE("error_code"),
ERROR_MESSAGE("error_message"),
IN_PROGRESS("in_progress"),
NAMES("names"),
NEXT("next"),
@ -77,24 +80,44 @@ public class Constants {
}
}
public enum ErrorCode {
BAD_OP("bad_op"),
BAD_REQUEST("bad_req"),
NO_SESSION("no_session"),
NOT_REGISTERED("not_registered"),
OP_NOT_SPECIFIED("op_not_spec"),
SESSION_EXPIRED("session_expired");
public enum ErrorCode implements Localizable {
BAD_OP("bad_op", "Invalid operation."),
BAD_REQUEST("bad_req", "Bad request."),
INVALID_NICK("invalid_nick", "Nickname must contain only upper and lower case letters, " +
"numbers, or underscores, must be 3 to 30 characters long, and must not start with a " +
"number."),
MESSAGE_TOO_LONG("msg_too_long", "Messages cannot be longer than 200 characters."),
NICK_IN_USE("nick_in_use", "Nickname is already in use."),
NO_MSG_SPECIFIED("no_msg_spec", "No message specified."),
NO_NICK_SPECIFIED("no_nick_spec", "No nickname specified."),
NO_SESSION("no_session", "Session not detected. Make sure you have cookies enabled."),
NOT_REGISTERED("not_registered", "Not registered. Refresh the page."),
OP_NOT_SPECIFIED("op_not_spec", "Operation not specified."),
SESSION_EXPIRED("session_expired", "Your session has expired. Refresh the page.");
private final String code;
private final String message;
ErrorCode(final String code) {
/**
* @param code
* Error code to send over the wire to the client.
* @param message
* Message the client should display for the error code.
*/
ErrorCode(final String code, final String message) {
this.code = code;
this.message = message;
}
@Override
public String toString() {
return code;
}
@Override
public String getString() {
return message;
}
}
public enum LongPollEvent {

View File

@ -6,12 +6,16 @@ import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import net.socialgamer.cah.Constants.Localizable;
public class UpdateJsConstants {
private static final String enumHeaderFmt = "cah.$.%s = function() {\r\n // pass\r\n};\r\n";
private static final String enumDummyFmt = "cah.$.%s.prototype.dummy = undefined;\r\n";
private static final String enumValueFmt = "cah.$.%s.%s = \"%s\";\r\n";
private static final String msgHeaderFmt = "cah.$.%s_msg = {};\r\n";
private static final String msgValueFmt = "cah.$.%s_msg['%s'] = \"%s\";\r\n";
/**
* @param args
@ -44,6 +48,15 @@ public class UpdateJsConstants {
final String value = values.get(key);
writer.format(enumValueFmt, cName, key, value);
}
if (Localizable.class.isAssignableFrom(c)) {
System.out.println(cName + "_msg");
writer.format(msgHeaderFmt, cName);
final Map<String, String> messages = getEnumMessageValues(c);
for (final String key : messages.keySet()) {
final String value = messages.get(key);
writer.format(msgValueFmt, cName, key, value);
}
}
writer.println();
}
writer.flush();
@ -66,6 +79,24 @@ public class UpdateJsConstants {
}
return enumMap;
}
private static Map<String, String> getEnumMessageValues(final Class enumClass)
throws IllegalArgumentException, IllegalAccessException {
if (!enumClass.isEnum()) {
throw new IllegalArgumentException(enumClass.getName() + " is not an enum");
} else if (!Localizable.class.isAssignableFrom(enumClass)) {
throw new IllegalArgumentException(enumClass.getName() + " does not implement Localizable.");
}
final Field[] flds = enumClass.getDeclaredFields();
final HashMap<String, String> messageMap = new HashMap<String, String>();
for (final Field f : flds) {
if (f.isEnumConstant()) {
messageMap.put(f.get(null).toString(), ((Localizable) f.get(null)).getString());
}
}
return messageMap;
}
}
////Automatically generated file. Do not edit!

View File

@ -6,6 +6,7 @@ import java.util.Map;
import javax.servlet.http.HttpSession;
import net.socialgamer.cah.Constants.AjaxOperation;
import net.socialgamer.cah.Constants.ErrorCode;
import net.socialgamer.cah.Constants.LongPollEvent;
import net.socialgamer.cah.Constants.LongPollResponse;
import net.socialgamer.cah.Constants.ReturnableData;
@ -37,11 +38,11 @@ public class ChatHandler extends Handler {
assert (user != null);
if (!parameters.containsKey("message") || parameters.get("message").length != 1) {
return error("No message specified.");
return error(ErrorCode.NO_MSG_SPECIFIED);
} else {
final String message = parameters.get("message")[0].trim();
if (message.length() > 200) {
return error("Messages cannot be longer than 200 characters.");
return error(ErrorCode.MESSAGE_TOO_LONG);
} else {
final HashMap<ReturnableData, Object> broadcastData = new HashMap<ReturnableData, Object>();
broadcastData.put(LongPollResponse.EVENT, LongPollEvent.CHAT.toString());

View File

@ -6,6 +6,7 @@ import java.util.Map;
import javax.servlet.http.HttpSession;
import net.socialgamer.cah.Constants.AjaxResponse;
import net.socialgamer.cah.Constants.ErrorCode;
import net.socialgamer.cah.Constants.ReturnableData;
@ -19,10 +20,11 @@ public abstract class Handler {
public abstract Map<ReturnableData, Object> handle(Map<String, String[]> parameters,
HttpSession session);
protected Map<ReturnableData, Object> error(final String message) {
protected Map<ReturnableData, Object> error(final ErrorCode errorCode) {
final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
data.put(AjaxResponse.ERROR, Boolean.TRUE);
data.put(AjaxResponse.ERROR_MESSAGE, message);
data.put(AjaxResponse.ERROR_CODE, errorCode.toString());
// data.put(AjaxResponse.ERROR_MESSAGE, message);
return data;
}
}

View File

@ -8,6 +8,7 @@ import javax.servlet.http.HttpSession;
import net.socialgamer.cah.Constants.AjaxOperation;
import net.socialgamer.cah.Constants.AjaxResponse;
import net.socialgamer.cah.Constants.ErrorCode;
import net.socialgamer.cah.Constants.ReturnableData;
import net.socialgamer.cah.Server;
import net.socialgamer.cah.data.ConnectedUsers;
@ -41,14 +42,13 @@ public class RegisterHandler extends Handler {
final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
if (!parameters.containsKey("nickname") || parameters.get("nickname").length != 1) {
return error("No nickname specified.");
return error(ErrorCode.NO_NICK_SPECIFIED);
} else {
final String nick = parameters.get("nickname")[0].trim();
if (!validName.matcher(nick).matches()) {
return error("Nickname must contain only upper and lower case letters, numbers, or"
+ " underscores, must be 3 to 30 characters long, and must not start with a number.");
return error(ErrorCode.INVALID_NICK);
} else if (users.hasUser(nick)) {
return error("Nickname " + nick + " already in use.");
return error(ErrorCode.NICK_IN_USE);
} else {
final User user = new User(nick);
users.newUser(user);