From 2e64e075f05baad305e1654ac4c680b60b9686da Mon Sep 17 00:00:00 2001 From: Jesper Blomquist Date: Tue, 27 Aug 2013 16:31:19 +0200 Subject: [PATCH] remove custom exception mapping and added jax-rs exception mappers --- .../cryson/service/CrysonFrontendService.java | 119 +++++------------- .../AccessDeniedExceptionMapper.java | 15 +++ .../CrysonExceptionMapper.java | 15 +++ .../CrysonExceptionMapperBase.java | 45 +++++++ .../DefaultExceptionMapper.java | 13 ++ ...ockingFailureExceptionExceptionMapper.java | 17 +++ .../OptimisticLockExceptionMapper.java | 18 +++ .../ParseExceptionMapper.java | 15 +++ .../StaleObjectStateExceptionMapper.java | 17 +++ .../cryson/spring/JettySpringHelper.java | 1 + .../sperber/cryson/apitest/CrysonAPITest.java | 12 +- 11 files changed, 194 insertions(+), 93 deletions(-) create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/AccessDeniedExceptionMapper.java create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapper.java create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapperBase.java create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/DefaultExceptionMapper.java create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/HibernateOptimisticLockingFailureExceptionExceptionMapper.java create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/OptimisticLockExceptionMapper.java create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/ParseExceptionMapper.java create mode 100644 server/src/main/java/se/sperber/cryson/service/exceptionmappers/StaleObjectStateExceptionMapper.java diff --git a/server/src/main/java/se/sperber/cryson/service/CrysonFrontendService.java b/server/src/main/java/se/sperber/cryson/service/CrysonFrontendService.java index 7169891..64b80a5 100644 --- a/server/src/main/java/se/sperber/cryson/service/CrysonFrontendService.java +++ b/server/src/main/java/se/sperber/cryson/service/CrysonFrontendService.java @@ -1,7 +1,7 @@ /* Cryson - Copyright 2011-2012 Björn Sperber (cryson@sperber.se) + Copyright 2011-2012 Bj��rn Sperber (cryson@sperber.se) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,29 +18,39 @@ package se.sperber.cryson.service; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.PostConstruct; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + import org.apache.log4j.Logger; -import org.hibernate.OptimisticLockException; -import org.hibernate.StaleObjectStateException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException; -import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; -import se.sperber.cryson.exception.CrysonEntityConflictException; -import se.sperber.cryson.exception.CrysonException; + import se.sperber.cryson.listener.CrysonListener; import se.sperber.cryson.listener.ListenerNotificationBatch; import se.sperber.cryson.serialization.CrysonSerializer; -import javax.annotation.PostConstruct; -import javax.ws.rs.*; -import javax.ws.rs.core.*; -import java.io.Serializable; -import java.text.ParseException; -import java.util.*; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; @Service @Path("/") @@ -68,28 +78,19 @@ public void findListeners() { @GET @Path("definition/{entity_name}") - public Response getEntityDefinition(@PathParam("entity_name") String entityName) { - try { + public Response getEntityDefinition(@PathParam("entity_name") String entityName) throws Throwable { return crysonService.getEntityDefinition(entityName); - } catch(Throwable t) { - return translateThrowable(t); - } } @GET @Path("definitions") - public Response getEntityDefinitions() { - try { + public Response getEntityDefinitions() throws Throwable { return crysonService.getEntityDefinitions(); - } catch(Throwable t) { - return translateThrowable(t); - } } @GET @Path("{entity_name}/{rawIds: [0-9,]+}") public Response getEntityById(@PathParam("entity_name") String entityName, @PathParam("rawIds") String rawIds, @QueryParam("fetch") String rawAssociationsToFetch) { - try { Set associationsToFetch = splitAssociationsToFetch(rawAssociationsToFetch); String[] stringIds = rawIds.split(","); @@ -102,75 +103,51 @@ public Response getEntityById(@PathParam("entity_name") String entityName, @Path } return crysonService.getEntitiesByIds(entityName, ids, associationsToFetch); } - } catch(Throwable t) { - return translateThrowable(t); - } } @GET @Path("{entity_name}") - public Response getEntitiesByExample(@PathParam("entity_name") String entityName, @QueryParam("example") String exampleJson, @QueryParam("fetch") String rawAssociationsToFetch) { - try { + public Response getEntitiesByExample(@PathParam("entity_name") String entityName, @QueryParam("example") String exampleJson, @QueryParam("fetch") String rawAssociationsToFetch) throws Throwable { Set associationsToFetch = splitAssociationsToFetch(rawAssociationsToFetch); return crysonService.getEntitiesByExample(entityName, exampleJson, associationsToFetch); - } catch(Throwable t) { - return translateThrowable(t); - } } @GET @Path("{entity_name}/all") public Response getAllEntities(@PathParam("entity_name") String entityName, @QueryParam("fetch") String rawAssociationsToFetch) { - try { Set associationsToFetch = splitAssociationsToFetch(rawAssociationsToFetch); return crysonService.getAllEntities(entityName, associationsToFetch); - } catch(Throwable t) { - return translateThrowable(t); - } } @GET @Path("namedQuery/{query_name}") public Response getEntitiesByNamedQuery(@PathParam("query_name") String queryName, @Context UriInfo uriInfo, @QueryParam("fetch") String rawAssociationsToFetch) { - try { Set associationsToFetch = splitAssociationsToFetch(rawAssociationsToFetch); MultivaluedMap queryParameters = uriInfo.getQueryParameters(); queryParameters.remove("fetch"); return crysonService.getEntitiesByNamedQuery(queryName, queryParameters, associationsToFetch); - } catch(Throwable t) { - return translateThrowable(t); - } } @POST @Path("namedQuery/{query_name}") public Response getEntitiesByNamedQueryPost(@PathParam("query_name") String queryName, @Context UriInfo uriInfo, @QueryParam("fetch") String rawAssociationsToFetch, String json) { - try { JsonElement parameters = crysonSerializer.parse(json); Set associationsToFetch = splitAssociationsToFetch(rawAssociationsToFetch); return crysonService.getEntitiesByNamedQueryJson(queryName, associationsToFetch, parameters); - } catch(Throwable t) { - return translateThrowable(t); - } } @PUT @Path("{entity_name}") - public Response createEntity(@Context UriInfo uriInfo, @Context HttpHeaders httpHeaders, @PathParam("entity_name") String entityName, String json) { - try { + public Response createEntity(@Context UriInfo uriInfo, @Context HttpHeaders httpHeaders, @PathParam("entity_name") String entityName, String json) throws Throwable { ListenerNotificationBatch listenerNotificationBatch = new ListenerNotificationBatch(uriInfo, httpHeaders); Response response = crysonService.createEntity(entityName, json, listenerNotificationBatch); notifyCommit(listenerNotificationBatch); return response; - } catch(Throwable t) { - return translateThrowable(t); - } } @POST @Path("commit") - public Response commit(@Context UriInfo uriInfo, @Context HttpHeaders httpHeaders, String json) { - try { + public Response commit(@Context UriInfo uriInfo, @Context HttpHeaders httpHeaders, String json) throws Throwable { ListenerNotificationBatch listenerNotificationBatch = new ListenerNotificationBatch(uriInfo, httpHeaders); JsonElement committedEntities = crysonSerializer.parse(json); crysonService.validatePermissions(committedEntities); @@ -182,20 +159,13 @@ public Response commit(@Context UriInfo uriInfo, @Context HttpHeaders httpHeader notifyCommit(listenerNotificationBatch); return response; - } catch(Throwable t) { - return translateThrowable(t); - } } public Response commitEntity(Object entity, UriInfo uriInfo, HttpHeaders httpHeaders) { - try { ListenerNotificationBatch listenerNotificationBatch = new ListenerNotificationBatch(uriInfo, httpHeaders); Response response = crysonService.commitEntity(entity, listenerNotificationBatch); notifyCommit(listenerNotificationBatch); return response; - } catch(Throwable t) { - return translateThrowable(t); - } } private void notifyCommit(ListenerNotificationBatch listenerNotificationBatch) { @@ -208,35 +178,6 @@ private void notifyCommit(ListenerNotificationBatch listenerNotificationBatch) { } } - private Response translateCrysonException(CrysonException e) { - String serializedMessage = crysonSerializer.serializeWithoutAugmentation(e.getSerializableMessage()); - return Response.status(e.getStatusCode()).entity(serializedMessage).build(); - } - - private Response translateThrowable(Throwable t) { - logger.error("Error", t); - if (t instanceof CrysonException) { - return translateCrysonException((CrysonException)t); - } else if (t instanceof OptimisticLockException || t instanceof HibernateOptimisticLockingFailureException || t instanceof StaleObjectStateException) { - return translateCrysonException(new CrysonEntityConflictException("Optimistic locking failed", t)); - } else if (t instanceof AccessDeniedException) { - return Response.status(Response.Status.UNAUTHORIZED).entity(buildJsonMessage(t.getMessage())).build(); - } else if (t instanceof ParseException) { - return Response.status(Response.Status.BAD_REQUEST).entity(buildJsonMessage(t.getMessage())).build(); - } else { - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(buildJsonMessage(t.getMessage())).build(); - } - } - - private String buildJsonMessage(String message) { - Map messageObject = new HashMap(); - if (message == null || message.equals("")) { - messageObject.put("message", "Unclassified error"); - } else { - messageObject.put("message", message); - } - return crysonSerializer.serializeWithoutAugmentation(messageObject); - } private Set splitAssociationsToFetch(String rawAssociationsToFetch) { if (rawAssociationsToFetch == null || rawAssociationsToFetch.equals("")) { diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/AccessDeniedExceptionMapper.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/AccessDeniedExceptionMapper.java new file mode 100644 index 0000000..c1d726f --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/AccessDeniedExceptionMapper.java @@ -0,0 +1,15 @@ +package se.sperber.cryson.service.exceptionmappers; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.springframework.security.access.AccessDeniedException; + +@Provider +public class AccessDeniedExceptionMapper extends CrysonExceptionMapperBase { + + @Override + protected Response toResponseInternal(AccessDeniedException e) { + return Response.status(Response.Status.UNAUTHORIZED).entity(buildJsonMessage(e.getMessage())).build(); + } +} diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapper.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapper.java new file mode 100644 index 0000000..a914d5e --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapper.java @@ -0,0 +1,15 @@ +package se.sperber.cryson.service.exceptionmappers; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import se.sperber.cryson.exception.CrysonException; + +@Provider +public class CrysonExceptionMapper extends CrysonExceptionMapperBase { + + @Override + protected Response toResponseInternal(CrysonException e) { + return translateCrysonException(e); + } +} diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapperBase.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapperBase.java new file mode 100644 index 0000000..9cdfaf6 --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/CrysonExceptionMapperBase.java @@ -0,0 +1,45 @@ +package se.sperber.cryson.service.exceptionmappers; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +import org.apache.log4j.Logger; + +import se.sperber.cryson.exception.CrysonException; +import se.sperber.cryson.initialization.Application; +import se.sperber.cryson.serialization.CrysonSerializer; + +public abstract class CrysonExceptionMapperBase implements ExceptionMapper { + + protected static final Logger logger = Logger.getLogger(CrysonExceptionMapperBase.class); + + private static CrysonSerializer crysonSerializer = Application.get(CrysonSerializer.class); + + @Override + public Response toResponse(E e) { + logger.error("Error", e); + return toResponseInternal(e); + } + + protected abstract Response toResponseInternal(E e); + + protected Response translateCrysonException(CrysonException e) { + String serializedMessage = crysonSerializer.serializeWithoutAugmentation(e.getSerializableMessage()); + return Response.status(e.getStatusCode()).entity(serializedMessage).build(); + } + + protected String buildJsonMessage(String message) { + Map messageObject = new HashMap(); + if (message == null || message.equals("")) { + messageObject.put("message", "Unclassified error"); + } + else { + messageObject.put("message", message); + } + return crysonSerializer.serializeWithoutAugmentation(messageObject); + } +} diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/DefaultExceptionMapper.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/DefaultExceptionMapper.java new file mode 100644 index 0000000..a327c2c --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/DefaultExceptionMapper.java @@ -0,0 +1,13 @@ +package se.sperber.cryson.service.exceptionmappers; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class DefaultExceptionMapper extends CrysonExceptionMapperBase { + + @Override + protected Response toResponseInternal(Throwable e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(buildJsonMessage(e.getMessage())).build(); + } +} diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/HibernateOptimisticLockingFailureExceptionExceptionMapper.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/HibernateOptimisticLockingFailureExceptionExceptionMapper.java new file mode 100644 index 0000000..7ae980d --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/HibernateOptimisticLockingFailureExceptionExceptionMapper.java @@ -0,0 +1,17 @@ +package se.sperber.cryson.service.exceptionmappers; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException; + +import se.sperber.cryson.exception.CrysonEntityConflictException; + +@Provider +public class HibernateOptimisticLockingFailureExceptionExceptionMapper extends CrysonExceptionMapperBase { + + @Override + protected Response toResponseInternal(HibernateOptimisticLockingFailureException e) { + return translateCrysonException(new CrysonEntityConflictException("Optimistic locking failed", e)); + } +} diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/OptimisticLockExceptionMapper.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/OptimisticLockExceptionMapper.java new file mode 100644 index 0000000..2e4c474 --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/OptimisticLockExceptionMapper.java @@ -0,0 +1,18 @@ +package se.sperber.cryson.service.exceptionmappers; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.hibernate.OptimisticLockException; + +import se.sperber.cryson.exception.CrysonEntityConflictException; + +@Provider +public class OptimisticLockExceptionMapper extends CrysonExceptionMapperBase { + + @Override + protected Response toResponseInternal(OptimisticLockException e) { + return translateCrysonException(new CrysonEntityConflictException("Optimistic locking failed", e)); + } + +} diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/ParseExceptionMapper.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/ParseExceptionMapper.java new file mode 100644 index 0000000..4b83f12 --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/ParseExceptionMapper.java @@ -0,0 +1,15 @@ +package se.sperber.cryson.service.exceptionmappers; + +import java.text.ParseException; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class ParseExceptionMapper extends CrysonExceptionMapperBase { + + @Override + protected Response toResponseInternal(ParseException e) { + return Response.status(Response.Status.BAD_REQUEST).entity(buildJsonMessage(e.getMessage())).build(); + } +} diff --git a/server/src/main/java/se/sperber/cryson/service/exceptionmappers/StaleObjectStateExceptionMapper.java b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/StaleObjectStateExceptionMapper.java new file mode 100644 index 0000000..bb1c68d --- /dev/null +++ b/server/src/main/java/se/sperber/cryson/service/exceptionmappers/StaleObjectStateExceptionMapper.java @@ -0,0 +1,17 @@ +package se.sperber.cryson.service.exceptionmappers; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.hibernate.StaleObjectStateException; + +import se.sperber.cryson.exception.CrysonEntityConflictException; + +@Provider +public class StaleObjectStateExceptionMapper extends CrysonExceptionMapperBase { + + @Override + protected Response toResponseInternal(StaleObjectStateException e) { + return translateCrysonException(new CrysonEntityConflictException("Optimistic locking failed", e)); + } +} diff --git a/server/src/main/java/se/sperber/cryson/spring/JettySpringHelper.java b/server/src/main/java/se/sperber/cryson/spring/JettySpringHelper.java index 52ad83a..120420c 100644 --- a/server/src/main/java/se/sperber/cryson/spring/JettySpringHelper.java +++ b/server/src/main/java/se/sperber/cryson/spring/JettySpringHelper.java @@ -76,6 +76,7 @@ public static class SpringComponentProviderFactory implements ResourceComponentP public static DefaultListableBeanFactory defaultListableBeanFactory; public ResourceComponentProvider getComponentProvider(final Class c) { + return new ResourceComponentProvider() { public Object getInstance() { diff --git a/server/src/test/java/se/sperber/cryson/apitest/CrysonAPITest.java b/server/src/test/java/se/sperber/cryson/apitest/CrysonAPITest.java index f896ae3..0b131b3 100644 --- a/server/src/test/java/se/sperber/cryson/apitest/CrysonAPITest.java +++ b/server/src/test/java/se/sperber/cryson/apitest/CrysonAPITest.java @@ -18,7 +18,12 @@ package se.sperber.cryson.apitest; -import com.google.gson.JsonElement; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.net.URLEncoder; + import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; @@ -33,14 +38,13 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; + import se.sperber.cryson.CrysonServer; import se.sperber.cryson.initialization.Application; import se.sperber.cryson.serialization.CrysonSerializer; import se.sperber.cryson.testutil.CrysonTestEntity; -import java.net.URLEncoder; - -import static org.junit.Assert.*; +import com.google.gson.JsonElement; public class CrysonAPITest {