Skip to content

Commit

Permalink
Merge 534205f into 2099306
Browse files Browse the repository at this point in the history
  • Loading branch information
acwwat committed Nov 17, 2016
2 parents 2099306 + 534205f commit d6c01f4
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 31 deletions.
4 changes: 4 additions & 0 deletions dropwizard-e2e/pom.xml
Expand Up @@ -49,6 +49,10 @@
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-client</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-views-mustache</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
30 changes: 27 additions & 3 deletions dropwizard-e2e/src/main/java/com/example/app1/App1.java
@@ -1,14 +1,25 @@
package com.example.app1;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

import com.github.mustachejava.MustacheNotFoundException;

import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.jersey.optional.EmptyOptionalException;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import io.dropwizard.views.ViewBundle;
import io.dropwizard.views.ViewRenderException;

public class App1 extends Application<Configuration> {
@Override
public void initialize(Bootstrap<Configuration> bootstrap) {
bootstrap.addBundle(new ViewBundle<Configuration>());
}

@Override
public void run(Configuration config, Environment env) throws Exception {
// Ensure that we can override the default 404 response on an
Expand All @@ -19,6 +30,19 @@ public Response toResponse(EmptyOptionalException exception) {
return Response.noContent().build();
}
});

// Ensure that we can override the 503 response of a view that refers to
// a missing Mustache template and return a 404 instead
env.jersey().register(new ExceptionMapper<WebApplicationException>() {
@Override
public Response toResponse(WebApplicationException exception) {
if (exception.getCause() instanceof ViewRenderException
&& exception.getCause().getCause() instanceof MustacheNotFoundException) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return exception.getResponse();
}
});

env.jersey().register(new App1Resource());
}
Expand Down
13 changes: 12 additions & 1 deletion dropwizard-e2e/src/main/java/com/example/app1/App1Resource.java
@@ -1,8 +1,11 @@
package com.example.app1;

import java.util.OptionalInt;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.OptionalInt;

import io.dropwizard.views.View;

@Path("/")
public class App1Resource {
Expand All @@ -11,4 +14,12 @@ public class App1Resource {
public OptionalInt emptyOptional() {
return OptionalInt.empty();
}

@GET
@Path("view-with-missing-tpl")
public View getMissingTemplateView() {
return new View("not-found.mustache") {
};
}

}
27 changes: 19 additions & 8 deletions dropwizard-e2e/src/test/java/com/example/app1/App1Test.java
@@ -1,16 +1,17 @@
package com.example.app1;

import io.dropwizard.Configuration;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.testing.ResourceHelpers;
import io.dropwizard.testing.junit.DropwizardAppRule;
import org.junit.ClassRule;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;

import javax.ws.rs.client.Client;
import javax.ws.rs.core.Response;

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.ClassRule;
import org.junit.Test;

import io.dropwizard.Configuration;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.testing.ResourceHelpers;
import io.dropwizard.testing.junit.DropwizardAppRule;

public class App1Test {
@ClassRule
Expand All @@ -19,10 +20,20 @@ public class App1Test {

@Test
public void custom204OnEmptyOptional() {
final Client client = new JerseyClientBuilder(RULE.getEnvironment()).build("test client");
final Client client = new JerseyClientBuilder(RULE.getEnvironment()).build("test client 1");
final String url = String.format("http://localhost:%d/empty-optional", RULE.getLocalPort());

final Response response = client.target(url).request().get();
assertThat(response.getStatus()).isEqualTo(204);
}

@Test
public void custom404OnViewRenderMissingMustacheTemplate() {
final Client client = new JerseyClientBuilder(RULE.getEnvironment()).build("test client 2");
final String url = String.format("http://localhost:%d/view-with-missing-tpl", RULE.getLocalPort());

final Response response = client.target(url).request().get();
assertThat(response.getStatus()).isEqualTo(404);
}

}
Expand Up @@ -7,9 +7,9 @@
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapperBuilder;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import io.dropwizard.views.View;
import io.dropwizard.views.ViewRenderException;
import io.dropwizard.views.ViewRenderer;

import java.io.IOException;
Expand Down Expand Up @@ -71,8 +71,8 @@ public void render(View view,
final Charset charset = view.getCharset().orElseGet(() -> Charset.forName(configuration.getEncoding(locale)));
final Template template = configuration.getTemplate(view.getTemplateName(), locale, charset.name());
template.process(view, new OutputStreamWriter(output, template.getEncoding()));
} catch (TemplateException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new ViewRenderException(e);
}
}

Expand Down
Expand Up @@ -4,6 +4,7 @@
import com.google.common.collect.ImmutableList;
import io.dropwizard.logging.BootstrapLogging;
import io.dropwizard.views.ViewMessageBodyWriter;
import io.dropwizard.views.ViewRenderExceptionMapper;
import io.dropwizard.views.ViewRenderer;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
Expand Down Expand Up @@ -58,6 +59,7 @@ protected Application configure() {
final ViewRenderer renderer = new FreemarkerViewRenderer();
config.register(new ViewMessageBodyWriter(new MetricRegistry(), ImmutableList.of(renderer)));
config.register(new ExampleResource());
config.register(new ViewRenderExceptionMapper());
return config;
}

Expand Down Expand Up @@ -87,7 +89,7 @@ public void returnsA500ForViewsWithBadTemplatePaths() throws Exception {
.isEqualTo(500);

assertThat(e.getResponse().readEntity(String.class))
.isEqualTo(ViewMessageBodyWriter.TEMPLATE_ERROR_MSG);
.isEqualTo(ViewRenderExceptionMapper.TEMPLATE_ERROR_MSG);
}
}

Expand All @@ -101,7 +103,7 @@ public void returnsA500ForViewsThatCantCompile() throws Exception {
.isEqualTo(500);

assertThat(e.getResponse().readEntity(String.class))
.isEqualTo(ViewMessageBodyWriter.TEMPLATE_ERROR_MSG);
.isEqualTo(ViewRenderExceptionMapper.TEMPLATE_ERROR_MSG);
}
}
}
Expand Up @@ -8,6 +8,7 @@
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.dropwizard.views.View;
import io.dropwizard.views.ViewRenderException;
import io.dropwizard.views.ViewRenderer;

import java.io.IOException;
Expand Down Expand Up @@ -52,7 +53,7 @@ public void render(View view, Locale locale, OutputStream output) throws IOExcep
template.execute(writer, view);
}
} catch (Throwable e) {
throw new RuntimeException("Mustache template error: " + view.getTemplateName(), e);
throw new ViewRenderException("Mustache template error: " + view.getTemplateName(), e);
}
}

Expand Down
Expand Up @@ -5,6 +5,7 @@
import com.google.common.collect.ImmutableMap;
import io.dropwizard.logging.BootstrapLogging;
import io.dropwizard.views.ViewMessageBodyWriter;
import io.dropwizard.views.ViewRenderExceptionMapper;
import io.dropwizard.views.ViewRenderer;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
Expand Down Expand Up @@ -60,6 +61,7 @@ protected Application configure() {
ResourceConfig config = new ResourceConfig();
final ViewRenderer renderer = new MustacheViewRenderer();
config.register(new ViewMessageBodyWriter(new MetricRegistry(), ImmutableList.of(renderer)));
config.register(new ViewRenderExceptionMapper());
config.register(new ExampleResource());
return config;
}
Expand All @@ -86,7 +88,7 @@ public void returnsA500ForViewsWithBadTemplatePaths() throws Exception {
.isEqualTo(500);

assertThat(e.getResponse().readEntity(String.class))
.isEqualTo(ViewMessageBodyWriter.TEMPLATE_ERROR_MSG);
.isEqualTo(ViewRenderExceptionMapper.TEMPLATE_ERROR_MSG);
}
}

Expand All @@ -100,7 +102,7 @@ public void returnsA500ForViewsThatCantCompile() throws Exception {
.isEqualTo(500);

assertThat(e.getResponse().readEntity(String.class))
.isEqualTo(ViewMessageBodyWriter.TEMPLATE_ERROR_MSG);
.isEqualTo(ViewRenderExceptionMapper.TEMPLATE_ERROR_MSG);
}
}

Expand Down
Expand Up @@ -12,7 +12,6 @@
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
Expand All @@ -29,11 +28,6 @@
@Produces({ MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML })
public class ViewMessageBodyWriter implements MessageBodyWriter<View> {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageBodyWriter.class);
public static final String TEMPLATE_ERROR_MSG =
"<html>" +
"<head><title>Template Error</title></head>" +
"<body><h1>Template Error</h1><p>Something went wrong rendering the page</p></body>" +
"</html>";

@Context
private HttpHeaders headers;
Expand Down Expand Up @@ -82,12 +76,9 @@ public void writeTo(View t,
}
}
throw new ViewRenderException("Unable to find a renderer for " + t.getTemplateName());
} catch (Exception e) {
} catch (ViewRenderException e) {
LOGGER.error("Template Error", e);
throw new WebApplicationException(Response.serverError()
.type(MediaType.TEXT_HTML_TYPE)
.entity(TEMPLATE_ERROR_MSG)
.build());
throw new WebApplicationException(e);
} finally {
context.stop();
}
Expand Down
Expand Up @@ -17,4 +17,46 @@ public class ViewRenderException extends IOException {
public ViewRenderException(String message) {
super(message);
}

/**
* Constructs an {@code ViewRenderException} with the specified detail
* message and cause.
*
* <p>
* Note that the detail message associated with {@code cause} is <i>not</i>
* automatically incorporated into this exception's detail message.
*
* @param message
* The detail message (which is saved for later retrieval by the
* {@link #getMessage()} method)
*
* @param cause
* The cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A null value is permitted, and
* indicates that the cause is nonexistent or unknown.)
*
* @since 1.1.0
*/
public ViewRenderException(String message, Throwable cause) {
super(message, cause);
}

/**
* Constructs an {@code ViewRenderException} with the specified cause and a
* detail message of {@code (cause==null ? null : cause.toString())} (which
* typically contains the class and detail message of {@code cause}). This
* constructor is useful for IO exceptions that are little more than
* wrappers for other throwables.
*
* @param cause
* The cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A null value is permitted, and
* indicates that the cause is nonexistent or unknown.)
*
* @since 1.1.0
*/
public ViewRenderException(Throwable cause) {
super(cause);
}

}
@@ -0,0 +1,38 @@
package io.dropwizard.views;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
* An {@link ExceptionMapper} that returns a 500 error response with a generic
* HTML error page when a {@link ViewRenderException} is thrown.
*
* @since 1.1.0
*/
@Provider
public class ViewRenderExceptionMapper implements ExceptionMapper<WebApplicationException> {

/**
* The generic HTML error page template.
*/
public static final String TEMPLATE_ERROR_MSG =
"<html>" +
"<head><title>Template Error</title></head>" +
"<body><h1>Template Error</h1><p>Something went wrong rendering the page</p></body>" +
"</html>";

@Override
public Response toResponse(WebApplicationException exception) {
if (exception.getCause() instanceof ViewRenderException) {
return Response.serverError()
.type(MediaType.TEXT_HTML_TYPE)
.entity(TEMPLATE_ERROR_MSG)
.build();
}
return exception.getResponse();
}

}

0 comments on commit d6c01f4

Please sign in to comment.