package kornell.api.client; import static com.google.gwt.http.client.Response.SC_CONFLICT; import static com.google.gwt.http.client.Response.SC_FORBIDDEN; import static com.google.gwt.http.client.Response.SC_INTERNAL_SERVER_ERROR; import static com.google.gwt.http.client.Response.SC_NOT_FOUND; import static com.google.gwt.http.client.Response.SC_NO_CONTENT; import static com.google.gwt.http.client.Response.SC_OK; import static com.google.gwt.http.client.Response.SC_SERVICE_UNAVAILABLE; import static com.google.gwt.http.client.Response.SC_UNAUTHORIZED; import java.util.logging.Logger; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.http.client.Response; import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONValue; import com.google.web.bindery.autobean.shared.AutoBean; import com.google.web.bindery.autobean.shared.AutoBeanCodex; import com.google.web.bindery.autobean.shared.AutoBeanFactory; import kornell.core.entity.EntityFactory; import kornell.core.error.KornellErrorTO; import kornell.core.event.EventFactory; import kornell.core.lom.LOMFactory; import kornell.core.to.TOFactory; import kornell.gui.client.GenericClientFactoryImpl; import kornell.gui.client.KornellConstantsHelper; import kornell.gui.client.event.LogoutEvent; @SuppressWarnings("unchecked") public abstract class Callback<T> implements RequestCallback { Logger logger = Logger.getLogger(Callback.class.getName()); @Override public void onResponseReceived(Request request, Response response) { int statusCode = response.getStatusCode(); switch (statusCode) { case SC_OK: ok(response); break; case SC_FORBIDDEN: forbidden(unwrapError(response)); break; case SC_UNAUTHORIZED: unauthorized(unwrapError(response)); break; case SC_CONFLICT: conflict(unwrapError(response)); break; case SC_NOT_FOUND: notFound(unwrapError(response)); break; case SC_NO_CONTENT: ok((T) null); break; case SC_INTERNAL_SERVER_ERROR: internalServerError(unwrapError(response)); break; case SC_SERVICE_UNAVAILABLE: serviceUnavailable(); break; case 0: failed(); break; default: logger.fine("Got a response, but don't know what to do about it"); break; } } protected void ok(Response response) { dispatchByMimeType(response); } private void dispatchByMimeType(Response response) { String contentTypeHeader = response.getHeader("Content-Type"); String contentType = contentTypeHeader.toLowerCase(); String responseText = response.getText(); if (contentType.equals("application/boolean")) { T bool = bool(responseText); ok(bool); } else if (contentType.contains("json")) { if (MediaTypes.get().containsType(contentType)) { Class<T> clazz = (Class<T>) MediaTypes.get().classOf(contentType); AutoBean<T> bean = null; AutoBeanFactory factory = factoryFor(contentType); if ("null".equals(responseText)) throw new NullPointerException( "The remote service returned 'null', this is probably a bug."); bean = AutoBeanCodex.decode(factory, clazz, responseText); T unwrapped = bean.as(); try { ok(unwrapped); } catch (ClassCastException ex) { String message = "Could not dispatch object of type [" + clazz.getName() + "] to this callback. Please check that your callback type mapping matches the response ContentType and you are hitting the correct URL."; throw new RuntimeException(message, ex); } } else ok(Callback.parseJson(responseText)); } else if (contentType.contains("application/octet-stream")) { T txt = (T) responseText; ok(txt); } else ok((T) null); // TODO: Consider throwing exception // "unknow response type" instead, but map "text/*" // and "application/*" first } private KornellErrorTO unwrapError(Response response) { String responseText = response.getText(); if (responseText == null || responseText.trim().equals("")) { throw new RuntimeException("Response text was blank"); } String contentType = response.getHeader("Content-Type").toLowerCase(); AutoBean<T> bean = null; AutoBeanFactory factory = factoryFor(contentType); if(factory == null){ KornellErrorTO errorTO = GenericClientFactoryImpl.TO_FACTORY.newKornellErrorTO().as(); errorTO.setMessageKey("genericUnhandledError"); return errorTO; } Class<T> clazz = (Class<T>) MediaTypes.get().classOf(contentType); bean = AutoBeanCodex.decode(factory, clazz, responseText); T unwrapped = bean.as(); if (unwrapped instanceof KornellErrorTO) { return (KornellErrorTO) unwrapped; } throw new RuntimeException("Supposed to get a KornellErrorTO, got " + unwrapped.getClass()); } protected void notFound(KornellErrorTO kornellErrorTO) { logger.fine(KornellConstantsHelper.getErrorMessage(kornellErrorTO)); } protected void internalServerError(KornellErrorTO kornellErrorTO) { logger.severe(KornellConstantsHelper .getErrorMessage(kornellErrorTO)); logger.severe("Cause: " + kornellErrorTO.getException()); } protected void serviceUnavailable() { } private T bool(String responseText) { return (T) Boolean.valueOf(responseText); } private AutoBeanFactory factoryFor(String contentType) { if (contentType == null) throw new NullPointerException( "Can't create factory without content type"); if (contentType.startsWith(TOFactory.PREFIX)) return GenericClientFactoryImpl.TO_FACTORY; else if (contentType.startsWith(LOMFactory.PREFIX)) return GenericClientFactoryImpl.LOM_FACTORY; else if (contentType.startsWith(EventFactory.PREFIX)) return GenericClientFactoryImpl.EVENT_FACTORY; else if (contentType.startsWith(EntityFactory.PREFIX)) return GenericClientFactoryImpl.ENTITY_FACTORY; else return null; } public abstract void ok(T to); protected void ok(JSONValue json) { } private static JSONValue parseJson(String jsonStr) { return JSONParser.parseStrict(jsonStr); } protected void failed() { logger.severe("Your request failed. Please check that the API is running and responding cross-origin requests."); } protected void unauthorized(KornellErrorTO kornellErrorTO) { GenericClientFactoryImpl.EVENT_BUS.fireEvent(new LogoutEvent()); logger.fine(KornellConstantsHelper .getErrorMessage(kornellErrorTO)); } protected void conflict(KornellErrorTO kornellErrorTO) { logger.info(KornellConstantsHelper.getErrorMessage(kornellErrorTO)); } protected void forbidden(KornellErrorTO kornellErrorTO) { if (kornellErrorTO != null) logger.fine(KornellConstantsHelper .getErrorMessage(kornellErrorTO)); } @Override public void onError(Request request, Throwable exception) { error(request, exception); } private void error(Request request, Throwable exception) { logger.severe("Error: " + exception.getMessage()); } }