private void writeHttpError(String message, OutputStream entityStream) { try { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); Template error; Template masterTemplate = null; try { masterTemplate = themeFileResolver.getIndexTemplate(webContext.getRequest().getBreakpoint()); } catch (TemplateNotFoundException e) { // Nothing doing } try { error = themeFileResolver.getTemplate("500.html", webContext.getRequest().getBreakpoint()); } catch (TemplateNotFoundException notFound) { // Fallback on the classpath hosted error 500 file error = new Template( "500", Resources.toString(Resources.getResource("templates/500.html"), Charsets.UTF_8)); } Map<String, Object> errorContext = Maps.newHashMap(); errorContext.put("error", message); engine.get().register(error); String rendered; if (masterTemplate != null) { errorContext.put("templateContent", error.getId()); errorContext.put("template", "500"); engine.get().register(masterTemplate); rendered = engine.get().render(masterTemplate.getId(), mapper.writeValueAsString(errorContext)); } else { rendered = engine.get().render(error.getId(), mapper.writeValueAsString(errorContext)); } entityStream.write(rendered.getBytes()); } catch (Exception e1) { throw new RuntimeException(e1); } }
private void writeDeveloperError(WebView webView, Exception e, OutputStream entityStream) { try { // Note: // This could be seen as a "server error", but we don't set the Status header to 500 because // we want to be // able to distinguish between actual server errors (internal Mayocat Shop server error) and // theme // developers errors (which this is). // This is comes at play when setting up monitoring with alerts on a number of 5xx response // above a // certain threshold. // Re-serialize the context as json with indentation for better debugging ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); Map<String, Object> context = webView.data(); String jsonContext = mapper.writeValueAsString(context); Template error = new Template( "developerError", Resources.toString( Resources.getResource("templates/developerError.html"), Charsets.UTF_8)); Map<String, Object> errorContext = Maps.newHashMap(); errorContext.put( "error", StringEscapeUtils.escapeXml(cleanErrorMessageForDisplay(e.getMessage()))); errorContext.put("stackTrace", StringEscapeUtils.escapeXml(ExceptionUtils.getStackTrace(e))); errorContext.put("context", StringEscapeUtils.escapeXml(jsonContext).trim()); errorContext.put("rawContext", jsonContext); errorContext.put("template", webView.template().toString()); engine.get().register(error); String rendered = engine.get().render(error.getId(), mapper.writeValueAsString(errorContext)); entityStream.write(rendered.getBytes()); } catch (Exception e1) { throw new RuntimeException(e1); } }
@Override public void writeTo( WebView webView, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { try { if (!mediaType.equals(MediaType.APPLICATION_JSON_TYPE) && webContext.getTheme() != null && !webContext.getTheme().isValidDefinition()) { // Fail fast with invalid theme error page, so that the developer knows ASAP and can correct // it. writeHttpError("Invalid theme definition", entityStream); return; } Template masterTemplate = null; try { masterTemplate = themeFileResolver.getIndexTemplate(webContext.getRequest().getBreakpoint()); } catch (TemplateNotFoundException e) { if (!mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) { // For JSON API calls, we don't care if the template is found or not. // For other calls, raise the exception throw e; } } Template template = null; String jsonContext = null; if (!mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) { if (webView.model().isPresent()) { // Check for a model Optional<String> path = themeFileResolver.resolveModelPath(webView.model().get()); if (path.isPresent()) { try { template = themeFileResolver.getTemplate( path.get(), webContext.getRequest().getBreakpoint()); } catch (TemplateNotFoundException e) { // Keep going } } // else just fallback on the default model } if (template == null) { try { template = themeFileResolver.getTemplate( webView.template().toString(), webContext.getRequest().getBreakpoint()); } catch (TemplateNotFoundException e) { if (webView.hasOption(WebView.Option.FALLBACK_ON_DEFAULT_THEME)) { try { template = themeFileResolver.getTemplate( themeManager.getDefaultTheme(), webView.template().toString(), webContext.getRequest().getBreakpoint()); } catch (TemplateNotFoundException e1) { // continue } } if (template == null && webView.hasOption(WebView.Option.FALLBACK_ON_GLOBAL_TEMPLATES)) { template = themeFileResolver.getGlobalTemplate( webView.template().toString(), webContext.getRequest().getBreakpoint()); } } } } if (!mediaType.equals(MediaType.APPLICATION_JSON_TYPE) || httpHeaders.containsKey("X-Mayocat-Full-Context")) { if (template != null) { webView.data().put("templateContent", template.getId()); webView.data().put("template", FilenameUtils.getBaseName(webView.template().toString())); } for (WebDataSupplier supplier : dataSuppliers.values()) { supplier.supply(webView.data()); } } try { ObjectMapper mapper = new ObjectMapper(); if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) { mapper.writeValue(entityStream, webView.data()); return; } if (template == null) { throw new TemplateNotFoundException(); } jsonContext = mapper.writeValueAsString(webView.data()); engine.get().register(template); engine.get().register(masterTemplate); String rendered = engine.get().render(masterTemplate.getId(), jsonContext); entityStream.write(rendered.getBytes()); } catch (JsonMappingException e) { this.logger.warn("Failed to serialize JSON context", e); writeDeveloperError(webView, e, entityStream); } catch (TemplateEngineException e) { writeDeveloperError(webView, e, entityStream); } } catch (TemplateNotFoundException e) { throw new WebApplicationException( Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Template not found : " + webView.template().toString()) .build()); } }