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:16 UTC
[1/2] syncope git commit: [SYNCOPE-152] Exception Mapper
Repository: syncope
Updated Branches:
refs/heads/2_0_X fac9d3d42 -> 781ae9367
refs/heads/master f04ab6c26 -> 68ab1fbed
[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/781ae936
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/781ae936
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/781ae936
Branch: refs/heads/2_0_X
Commit: 781ae9367ef09bc74bc3c3e5d2007857d0ddc57d
Parents: fac9d3d
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:46:57 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/781ae936/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/781ae936/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/781ae936/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/781ae936/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/781ae936/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/781ae936/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/781ae936/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/781ae936/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/781ae936/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 660a9d6..2031da8 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 {
Assume.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 {
Assume.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 {
Assume.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 {
Assume.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 {
Assume.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), ";"));
[2/2] syncope git commit: [SYNCOPE-152] Exception Mapper
Posted by il...@apache.org.
[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), ";"));