You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2017/10/31 17:47:17 UTC

[2/2] syncope git commit: [SYNCOPE-152] Exception Mapper

[SYNCOPE-152] Exception Mapper


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/68ab1fbe
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/68ab1fbe
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/68ab1fbe

Branch: refs/heads/master
Commit: 68ab1fbed71e60ae9f4abbd8c89456a6e6849af3
Parents: f04ab6c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Oct 31 18:46:57 2017 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Oct 31 18:47:06 2017 +0100

----------------------------------------------------------------------
 .../ext/scimv2/api/ConflictException.java       |  36 ++++
 .../scimv2/api/PayloadTooLargeException.java    |  36 ++++
 .../ext/scimv2/api/SCIMBadRequestException.java |  44 ++++
 .../syncope/ext/scimv2/api/data/SCIMError.java  |  68 ++++++
 .../syncope/ext/scimv2/api/type/ErrorType.java  |  33 +++
 .../syncope/ext/scimv2/api/type/Resource.java   |   3 +-
 .../ext/scimv2/cxf/SCIMExceptionMapper.java     | 210 +++++++++++++++++++
 .../src/main/resources/restSCIMv2CXFContext.xml |   3 +
 .../org/apache/syncope/fit/core/SCIMITCase.java |  17 +-
 9 files changed, 442 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/ConflictException.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/ConflictException.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/ConflictException.java
new file mode 100644
index 0000000..d211a35
--- /dev/null
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/ConflictException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.api;
+
+import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.core.Response;
+
+public class ConflictException extends ClientErrorException {
+
+    private static final long serialVersionUID = -6845464464868163175L;
+
+    public ConflictException() {
+        super(Response.Status.CONFLICT);
+    }
+
+    public ConflictException(final String message) {
+        super(message, Response.Status.CONFLICT);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/PayloadTooLargeException.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/PayloadTooLargeException.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/PayloadTooLargeException.java
new file mode 100644
index 0000000..e6ab195
--- /dev/null
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/PayloadTooLargeException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.api;
+
+import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.core.Response;
+
+public class PayloadTooLargeException extends ClientErrorException {
+
+    private static final long serialVersionUID = -3980136349506530672L;
+
+    public PayloadTooLargeException() {
+        super(Response.Status.REQUEST_ENTITY_TOO_LARGE);
+    }
+
+    public PayloadTooLargeException(final String message) {
+        super(message, Response.Status.REQUEST_ENTITY_TOO_LARGE);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/SCIMBadRequestException.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/SCIMBadRequestException.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/SCIMBadRequestException.java
new file mode 100644
index 0000000..4b36798
--- /dev/null
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/SCIMBadRequestException.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.api;
+
+import javax.ws.rs.BadRequestException;
+import org.apache.syncope.ext.scimv2.api.type.ErrorType;
+
+public class SCIMBadRequestException extends BadRequestException {
+
+    private static final long serialVersionUID = -2588839750716910491L;
+
+    private final ErrorType errorType;
+
+    public SCIMBadRequestException(final ErrorType errorType) {
+        super();
+        this.errorType = errorType;
+    }
+
+    public SCIMBadRequestException(final ErrorType errorType, final String detail) {
+        super(detail);
+        this.errorType = errorType;
+    }
+
+    public ErrorType getErrorType() {
+        return errorType;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
new file mode 100644
index 0000000..d7112d1
--- /dev/null
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.api.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonFormat.Shape;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.syncope.ext.scimv2.api.type.ErrorType;
+import org.apache.syncope.ext.scimv2.api.type.Resource;
+
+public class SCIMError extends SCIMBean {
+
+    private static final long serialVersionUID = -8836902509266522394L;
+
+    private final List<String> schemas = Arrays.asList(Resource.Error.schema());
+
+    private final ErrorType scimType;
+
+    private final String detail;
+
+    @JsonFormat(shape = Shape.STRING)
+    private final int status = 400;
+
+    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+    public SCIMError(
+            @JsonProperty("scimType") final ErrorType scimType,
+            @JsonProperty("detail") final String detail) {
+
+        this.scimType = scimType;
+        this.detail = detail;
+    }
+
+    public List<String> getSchemas() {
+        return schemas;
+    }
+
+    public ErrorType getScimType() {
+        return scimType;
+    }
+
+    public String getDetail() {
+        return detail;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/ErrorType.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/ErrorType.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/ErrorType.java
new file mode 100644
index 0000000..bbd7489
--- /dev/null
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/ErrorType.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.api.type;
+
+public enum ErrorType {
+    invalidFilter,
+    tooMany,
+    uniqueness,
+    mutability,
+    invalidSyntax,
+    invalidPath,
+    noTarget,
+    invalidValue,
+    invalidVers,
+    sensitive;
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java
index 37b15b6..8fb2bde 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/type/Resource.java
@@ -26,7 +26,8 @@ public enum Resource {
     User("urn:ietf:params:scim:schemas:core:2.0:User"),
     EnterpriseUser("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"),
     Group("urn:ietf:params:scim:schemas:core:2.0:Group"),
-    ListResponse("urn:ietf:params:scim:api:messages:2.0:ListResponse");
+    ListResponse("urn:ietf:params:scim:api:messages:2.0:ListResponse"),
+    Error("urn:ietf:params:scim:api:messages:2.0:Error");
 
     private final String schema;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
new file mode 100644
index 0000000..e525e77
--- /dev/null
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.scimv2.cxf;
+
+import java.util.Map;
+import java.util.Set;
+import javax.validation.ValidationException;
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
+import org.apache.syncope.core.persistence.api.dao.DuplicateException;
+import org.apache.syncope.core.persistence.api.dao.MalformedPathException;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
+import org.apache.syncope.core.workflow.api.WorkflowException;
+import org.apache.syncope.ext.scimv2.api.ConflictException;
+import org.apache.syncope.ext.scimv2.api.PayloadTooLargeException;
+import org.apache.syncope.ext.scimv2.api.data.SCIMError;
+import org.apache.syncope.ext.scimv2.api.type.ErrorType;
+import org.identityconnectors.framework.common.exceptions.ConfigurationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.transaction.TransactionSystemException;
+
+@Provider
+public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SCIMExceptionMapper.class);
+
+    private static Class<?> ENTITYEXISTS_EXCLASS = null;
+
+    private static Class<?> PERSISTENCE_EXCLASS = null;
+
+    private static Class<?> ROLLBACK_EXCLASS = null;
+
+    private static Class<?> JPASYSTEM_EXCLASS = null;
+
+    private static Class<?> CONNECTOR_EXCLASS = null;
+
+    private static Class<?> IBATISPERSISTENCE_EXCLASS = null;
+
+    static {
+        try {
+            ENTITYEXISTS_EXCLASS = Class.forName("javax.persistence.EntityExistsException");
+            PERSISTENCE_EXCLASS = Class.forName("javax.persistence.PersistenceException");
+            ROLLBACK_EXCLASS = Class.forName("javax.persistence.RollbackException");
+            JPASYSTEM_EXCLASS = Class.forName("org.springframework.orm.jpa.JpaSystemException");
+            CONNECTOR_EXCLASS = Class.forName("org.identityconnectors.framework.common.exceptions.ConnectorException");
+            IBATISPERSISTENCE_EXCLASS = Class.forName("org.apache.ibatis.exceptions.PersistenceException");
+        } catch (ClassNotFoundException e) {
+            // ignore
+        }
+    }
+
+    @Override
+    public Response toResponse(final Exception ex) {
+        LOG.error("Exception thrown", ex);
+
+        ResponseBuilder builder;
+
+        if (ex instanceof AccessDeniedException
+                || ex instanceof ForbiddenException
+                || ex instanceof NotAuthorizedException
+                || ex instanceof NotFoundException
+                || ex instanceof ConflictException
+                || ex instanceof PayloadTooLargeException) {
+
+            // leaves the default exception processing
+            builder = null;
+        } else if (ex instanceof SyncopeClientException) {
+            SyncopeClientException sce = (SyncopeClientException) ex;
+            builder = builder(sce.getType(), ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ex instanceof DelegatedAdministrationException
+                || ExceptionUtils.getRootCause(ex) instanceof DelegatedAdministrationException) {
+
+            builder = builder(ClientExceptionType.DelegatedAdministration, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getClass())
+                || ex instanceof DuplicateException
+                || PERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass())
+                && ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getCause().getClass())) {
+
+            builder = builder(ClientExceptionType.EntityExists, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ex instanceof DataIntegrityViolationException
+                || JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
+
+            builder = builder(ClientExceptionType.DataIntegrityViolation, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (CONNECTOR_EXCLASS.isAssignableFrom(ex.getClass())) {
+            builder = builder(ClientExceptionType.ConnectorException, ExceptionUtils.getRootCauseMessage(ex));
+        } else {
+            builder = processInvalidEntityExceptions(ex);
+            if (builder == null) {
+                builder = processBadRequestExceptions(ex);
+            }
+            // process JAX-RS validation errors
+            if (builder == null && ex instanceof ValidationException) {
+                builder = builder(ClientExceptionType.RESTValidation, ExceptionUtils.getRootCauseMessage(ex));
+            }
+            // ...or just report as InternalServerError
+            if (builder == null) {
+                builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
+            }
+        }
+
+        return builder == null ? null : builder.build();
+    }
+
+    private ResponseBuilder processInvalidEntityExceptions(final Exception ex) {
+        InvalidEntityException iee = null;
+
+        if (ex instanceof InvalidEntityException) {
+            iee = (InvalidEntityException) ex;
+        }
+        if (ex instanceof TransactionSystemException && ROLLBACK_EXCLASS.isAssignableFrom(ex.getCause().getClass())
+                && ex.getCause().getCause() instanceof InvalidEntityException) {
+
+            iee = (InvalidEntityException) ex.getCause().getCause();
+        }
+
+        if (iee != null) {
+            ClientExceptionType exType;
+            if (iee.getEntityClassSimpleName().endsWith("Policy")) {
+                exType = ClientExceptionType.InvalidPolicy;
+            } else if (iee.getEntityClassSimpleName().equals(PlainAttr.class.getSimpleName())) {
+                exType = ClientExceptionType.InvalidValues;
+            } else {
+                try {
+                    exType = ClientExceptionType.valueOf("Invalid" + iee.getEntityClassSimpleName());
+                } catch (IllegalArgumentException e) {
+                    // ignore
+                    exType = ClientExceptionType.InvalidEntity;
+                }
+            }
+
+            StringBuilder msg = new StringBuilder();
+
+            for (Map.Entry<Class<?>, Set<EntityViolationType>> violation : iee.getViolations().entrySet()) {
+                for (EntityViolationType violationType : violation.getValue()) {
+                    msg.append(violationType.name()).append(": ").append(violationType.getMessage()).append('\n');
+                }
+            }
+
+            return builder(exType, msg.toString());
+        }
+
+        return null;
+    }
+
+    private ResponseBuilder processBadRequestExceptions(final Exception ex) {
+        if (ex instanceof WorkflowException) {
+            return builder(ClientExceptionType.Workflow, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (PERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass())) {
+            return builder(ClientExceptionType.GenericPersistence, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (IBATISPERSISTENCE_EXCLASS != null && IBATISPERSISTENCE_EXCLASS.isAssignableFrom(ex.getClass())) {
+            return builder(ClientExceptionType.Workflow, "Currently unavailable. Please try later.");
+        } else if (JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
+            return builder(ClientExceptionType.DataIntegrityViolation, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ex instanceof ConfigurationException) {
+            return builder(ClientExceptionType.InvalidConnIdConf, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ex instanceof ParsingValidationException) {
+            return builder(ClientExceptionType.InvalidValues, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ex instanceof MalformedPathException) {
+            return builder(ClientExceptionType.InvalidPath, ExceptionUtils.getRootCauseMessage(ex));
+        }
+
+        return null;
+    }
+
+    private ResponseBuilder builder(final ClientExceptionType hType, final String msg) {
+        ResponseBuilder builder = Response.status(hType.getResponseStatus());
+
+        if (hType.getResponseStatus() == Response.Status.BAD_REQUEST) {
+            ErrorType scimType = null;
+            if (hType.name().startsWith("Invalid") || hType == ClientExceptionType.RESTValidation) {
+                scimType = ErrorType.invalidValue;
+            }
+
+            builder = builder.entity(new SCIMError(scimType, msg));
+        }
+
+        return builder;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/ext/scimv2/scim-rest-cxf/src/main/resources/restSCIMv2CXFContext.xml
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-cxf/src/main/resources/restSCIMv2CXFContext.xml b/ext/scimv2/scim-rest-cxf/src/main/resources/restSCIMv2CXFContext.xml
index ebbbe6e..cb306bf 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/resources/restSCIMv2CXFContext.xml
+++ b/ext/scimv2/scim-rest-cxf/src/main/resources/restSCIMv2CXFContext.xml
@@ -35,6 +35,8 @@ under the License.
   
   <bean id="jsonProvider" class="org.apache.syncope.ext.scimv2.cxf.JacksonSCIMJsonProvider"/>
 
+  <bean id="exceptionMapper" class="org.apache.syncope.ext.scimv2.cxf.SCIMExceptionMapper"/>
+
   <bean id="validationProvider" class="org.apache.cxf.validation.BeanValidationProvider"/>
   <bean id="validationInInterceptor" class="org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor">
     <property name="provider" ref="validationProvider"/>
@@ -78,6 +80,7 @@ under the License.
     </jaxrs:outInterceptors>
     <jaxrs:providers>
       <ref bean="jsonProvider"/>
+      <ref bean="exceptionMapper"/>
       <ref bean="addETagFilter"/>
       <ref bean="wadlGenerator"/>
     </jaxrs:providers>

http://git-wip-us.apache.org/repos/asf/syncope/blob/68ab1fbe/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
index 0e85639..ecc75ba 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
@@ -61,7 +61,7 @@ public class SCIMITCase extends AbstractITCase {
         assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
 
         Response response = webClient().path("ServiceProviderConfig").get();
-        assertEquals(200, response.getStatus());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
                 SCIMConstants.APPLICATION_SCIM_JSON,
                 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
@@ -76,7 +76,7 @@ public class SCIMITCase extends AbstractITCase {
         assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
 
         Response response = webClient().path("ResourceTypes").get();
-        assertEquals(200, response.getStatus());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
                 SCIMConstants.APPLICATION_SCIM_JSON,
                 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
@@ -87,7 +87,7 @@ public class SCIMITCase extends AbstractITCase {
         assertEquals(2, resourceTypes.size());
 
         response = webClient().path("ResourceTypes").path("User").get();
-        assertEquals(200, response.getStatus());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
 
         ResourceType user = response.readEntity(ResourceType.class);
         assertNotNull(user);
@@ -100,7 +100,7 @@ public class SCIMITCase extends AbstractITCase {
         assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
 
         Response response = webClient().path("Schemas").get();
-        assertEquals(200, response.getStatus());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
                 SCIMConstants.APPLICATION_SCIM_JSON,
                 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
@@ -109,8 +109,11 @@ public class SCIMITCase extends AbstractITCase {
         assertNotNull(schemas);
         assertEquals(3, schemas.size());
 
+        response = webClient().path("Schemas").path("none").get();
+        assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
+
         response = webClient().path("Schemas").path(Resource.EnterpriseUser.schema()).get();
-        assertEquals(200, response.getStatus());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
 
         ObjectNode enterpriseUser = response.readEntity(ObjectNode.class);
         assertNotNull(enterpriseUser);
@@ -122,7 +125,7 @@ public class SCIMITCase extends AbstractITCase {
         assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
 
         Response response = webClient().path("Users").path("1417acbe-cbf6-4277-9372-e75e04f97000").get();
-        assertEquals(200, response.getStatus());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
                 SCIMConstants.APPLICATION_SCIM_JSON,
                 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
@@ -140,7 +143,7 @@ public class SCIMITCase extends AbstractITCase {
         assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
 
         Response response = webClient().path("Groups").get();
-        assertEquals(200, response.getStatus());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
                 SCIMConstants.APPLICATION_SCIM_JSON,
                 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));