| /* |
| * 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.directory.scim.server.rest; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.net.URI; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Optional; |
| import java.util.Set; |
| |
| import jakarta.enterprise.inject.spi.CDI; |
| import jakarta.ws.rs.WebApplicationException; |
| import jakarta.ws.rs.core.EntityTag; |
| import jakarta.ws.rs.core.Response; |
| import jakarta.ws.rs.core.Response.ResponseBuilder; |
| import jakarta.ws.rs.core.Response.Status; |
| import jakarta.ws.rs.core.Response.Status.Family; |
| |
| import org.apache.directory.scim.server.exception.*; |
| import org.apache.directory.scim.core.repository.RepositoryRegistry; |
| import org.apache.directory.scim.core.repository.Repository; |
| import org.apache.directory.scim.core.schema.SchemaRegistry; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.fasterxml.jackson.core.JsonProcessingException; |
| |
| import org.apache.directory.scim.core.repository.UpdateRequest; |
| import org.apache.directory.scim.core.repository.annotations.ScimProcessingExtension; |
| import org.apache.directory.scim.core.repository.extensions.AttributeFilterExtension; |
| import org.apache.directory.scim.core.repository.extensions.ProcessingExtension; |
| import org.apache.directory.scim.spec.filter.attribute.ScimRequestContext; |
| import org.apache.directory.scim.core.repository.extensions.ClientFilterException; |
| import org.apache.directory.scim.spec.adapter.FilterWrapper; |
| import org.apache.directory.scim.spec.protocol.BaseResourceTypeResource; |
| import org.apache.directory.scim.spec.protocol.ErrorMessageType; |
| import org.apache.directory.scim.spec.filter.attribute.AttributeReference; |
| import org.apache.directory.scim.spec.filter.attribute.AttributeReferenceListWrapper; |
| import org.apache.directory.scim.spec.protocol.data.ErrorResponse; |
| import org.apache.directory.scim.spec.protocol.data.ListResponse; |
| import org.apache.directory.scim.spec.protocol.data.PatchRequest; |
| import org.apache.directory.scim.spec.protocol.data.SearchRequest; |
| import org.apache.directory.scim.spec.filter.FilterResponse; |
| import org.apache.directory.scim.spec.filter.Filter; |
| import org.apache.directory.scim.spec.filter.PageRequest; |
| import org.apache.directory.scim.spec.filter.SortOrder; |
| import org.apache.directory.scim.spec.filter.SortRequest; |
| import org.apache.directory.scim.spec.resources.ScimResource; |
| import lombok.extern.slf4j.Slf4j; |
| |
| @Slf4j |
| public abstract class BaseResourceTypeResourceImpl<T extends ScimResource> implements BaseResourceTypeResource<T> { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(BaseResourceTypeResourceImpl.class); |
| |
| private final SchemaRegistry schemaRegistry; |
| |
| private final RepositoryRegistry repositoryRegistry; |
| |
| private final AttributeUtil attributeUtil; |
| |
| RequestContext requestContext; |
| |
| private final EtagGenerator etagGenerator; |
| |
| private final Class<T> resourceClass; |
| |
| public BaseResourceTypeResourceImpl(SchemaRegistry schemaRegistry, RepositoryRegistry repositoryRegistry, RequestContext requestContext, EtagGenerator etagGenerator, Class<T> resourceClass) { |
| this.schemaRegistry = schemaRegistry; |
| this.repositoryRegistry = repositoryRegistry; |
| this.requestContext = requestContext; |
| this.etagGenerator = etagGenerator; |
| this.resourceClass = resourceClass; |
| this.attributeUtil = new AttributeUtil(schemaRegistry); |
| } |
| |
| public Repository<T> getRepository() { |
| return repositoryRegistry.getRepository(resourceClass); |
| } |
| |
| Repository<T> getRepositoryInternal() throws ScimServerException { |
| Repository<T> repository = getRepository(); |
| if (repository == null) { |
| throw new ScimServerException(Status.INTERNAL_SERVER_ERROR, "Provider not defined"); |
| } |
| return repository; |
| } |
| |
| @Override |
| public Response getById(String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) { |
| if (requestContext.getUriInfo().getQueryParameters().getFirst("filter") != null) { |
| return Response.status(Status.FORBIDDEN) |
| .build(); |
| } |
| |
| try { |
| Repository<T> repository = getRepositoryInternal(); |
| |
| T resource = null; |
| try { |
| resource = repository.get(id); |
| } catch (UnableToRetrieveResourceException e2) { |
| Status status = Status.fromStatusCode(e2.getStatus()); |
| if (status.getFamily() |
| .equals(Family.SERVER_ERROR)) { |
| return createGenericExceptionResponse(e2, status); |
| } |
| } catch (Exception e) { |
| log.error("Uncaught repository exception", e); |
| |
| return handleException(e); |
| } |
| |
| if (resource != null) { |
| EntityTag backingETag = null; |
| try { |
| backingETag = etagGenerator.generateEtag(resource); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e1) { |
| return createETagErrorResponse(); |
| } |
| |
| ResponseBuilder evaluatePreconditionsResponse = requestContext.getRequest().evaluatePreconditions(backingETag); |
| |
| if (evaluatePreconditionsResponse != null) { |
| return Response.status(Status.NOT_MODIFIED) |
| .build(); |
| } |
| } |
| |
| Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| |
| if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) { |
| return createAmbiguousAttributeParametersResponse(); |
| } |
| |
| if (resource == null) { |
| return createNotFoundResponse(id); |
| } |
| |
| EntityTag etag = null; |
| |
| try { |
| etag = etagGenerator.generateEtag(resource); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) { |
| return createETagErrorResponse(); |
| } |
| |
| // Process Attributes |
| try { |
| resource = processFilterAttributeExtensions(repository, resource, attributeReferences, excludedAttributeReferences); |
| } catch (ClientFilterException e1) { |
| ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage()); |
| return er.toResponse(); |
| } |
| |
| try { |
| if (!excludedAttributeReferences.isEmpty()) { |
| resource = attributeUtil.setExcludedAttributesForDisplay(resource, excludedAttributeReferences); |
| } else { |
| resource = attributeUtil.setAttributesForDisplay(resource, attributeReferences); |
| } |
| |
| return Response.ok() |
| .entity(resource) |
| .location(buildLocationTag(resource)) |
| .tag(etag) |
| .build(); |
| } catch (AttributeException e) { |
| return createAttributeProcessingErrorResponse(e); |
| } |
| } catch (ScimServerException sse) { |
| LOG.error("Error Processing SCIM Request", sse); |
| return sse.getErrorResponse() |
| .toResponse(); |
| } |
| |
| } |
| |
| @Override |
| public Response query(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes, FilterWrapper filter, AttributeReference sortBy, SortOrder sortOrder, Integer startIndex, Integer count) { |
| SearchRequest searchRequest = new SearchRequest(); |
| searchRequest.setAttributes(Optional.ofNullable(attributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet())); |
| searchRequest.setExcludedAttributes(Optional.ofNullable(excludedAttributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet())); |
| |
| if (filter != null) { |
| searchRequest.setFilter(filter.getFilter()); |
| } |
| else { |
| searchRequest.setFilter(null); |
| } |
| |
| searchRequest.setSortBy(sortBy); |
| searchRequest.setSortOrder(sortOrder); |
| searchRequest.setStartIndex(startIndex); |
| searchRequest.setCount(count); |
| |
| return find(searchRequest); |
| } |
| |
| @Override |
| public Response create(T resource, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) { |
| try { |
| Repository<T> repository = getRepositoryInternal(); |
| |
| Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| |
| if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) { |
| return createAmbiguousAttributeParametersResponse(); |
| } |
| |
| T created; |
| try { |
| created = repository.create(resource); |
| } catch (UnableToCreateResourceException e1) { |
| Status status = Status.fromStatusCode(e1.getStatus()); |
| ErrorResponse er = new ErrorResponse(status, "Error"); |
| |
| if (status == Status.CONFLICT) { |
| er.setScimType(ErrorMessageType.UNIQUENESS); |
| |
| //only use default error message if the ErrorResponse does not already contain a message |
| if (e1.getMessage() == null) { |
| er.setDetail(ErrorMessageType.UNIQUENESS.getDetail()); |
| } else { |
| er.setDetail(e1.getMessage()); |
| } |
| } else { |
| er.setDetail(e1.getMessage()); |
| } |
| |
| return er.toResponse(); |
| } catch (Exception e) { |
| log.error("Uncaught repository exception", e); |
| |
| return handleException(e); |
| } |
| |
| EntityTag etag = null; |
| try { |
| etag = etagGenerator.generateEtag(created); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) { |
| log.error("Failed to generate etag for newly created entity " + e.getMessage()); |
| } |
| |
| // Process Attributes |
| try { |
| created = processFilterAttributeExtensions(repository, created, attributeReferences, excludedAttributeReferences); |
| } catch (ClientFilterException e1) { |
| ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage()); |
| return er.toResponse(); |
| } |
| |
| try { |
| if (!excludedAttributeReferences.isEmpty()) { |
| created = attributeUtil.setExcludedAttributesForDisplay(created, excludedAttributeReferences); |
| } else { |
| created = attributeUtil.setAttributesForDisplay(created, attributeReferences); |
| } |
| } catch (AttributeException e) { |
| if (etag == null) { |
| return Response.status(Status.CREATED) |
| .location(buildLocationTag(created)) |
| .build(); |
| } else { |
| Response.status(Status.CREATED) |
| .location(buildLocationTag(created)) |
| .tag(etag) |
| .build(); |
| } |
| } |
| |
| // TODO - Is this the right behavior? |
| if (etag == null) { |
| return Response.status(Status.CREATED) |
| .location(buildLocationTag(created)) |
| .entity(created) |
| .build(); |
| } |
| |
| return Response.status(Status.CREATED) |
| .location(buildLocationTag(created)) |
| .tag(etag) |
| .entity(created) |
| .build(); |
| } catch (ScimServerException sse) { |
| LOG.error("Error Processing SCIM Request", sse); |
| return sse.getErrorResponse() |
| .toResponse(); |
| } |
| } |
| |
| @Override |
| public Response find(SearchRequest request) { |
| try { |
| Repository<T> repository = getRepositoryInternal(); |
| |
| Set<AttributeReference> attributeReferences = Optional.ofNullable(request.getAttributes()) |
| .orElse(Collections.emptySet()); |
| Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(request.getExcludedAttributes()) |
| .orElse(Collections.emptySet()); |
| if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) { |
| return createAmbiguousAttributeParametersResponse(); |
| } |
| |
| Filter filter = request.getFilter(); |
| PageRequest pageRequest = request.getPageRequest(); |
| SortRequest sortRequest = request.getSortRequest(); |
| |
| ListResponse<T> listResponse = new ListResponse<>(); |
| |
| FilterResponse<T> filterResp = null; |
| try { |
| filterResp = repository.find(filter, pageRequest, sortRequest); |
| } catch (UnableToRetrieveResourceException e1) { |
| log.info("Caught an UnableToRetrieveResourceException " + e1.getMessage() + " : " + e1.getStatus()); |
| return createGenericExceptionResponse(e1, e1.getStatus()); |
| } catch (Exception e) { |
| log.error("Uncaught repository exception", e); |
| |
| return handleException(e); |
| } |
| |
| // If no resources are found, we should still return a ListResponse with |
| // the totalResults set to 0; |
| // (https://tools.ietf.org/html/rfc7644#section-3.4.2) |
| if (filterResp == null || filterResp.getResources() == null || filterResp.getResources() |
| .isEmpty()) { |
| listResponse.setTotalResults(0); |
| } else { |
| log.info("Find returned " + filterResp.getResources() |
| .size()); |
| listResponse.setItemsPerPage(filterResp.getResources() |
| .size()); |
| listResponse.setStartIndex(1); |
| listResponse.setTotalResults(filterResp.getResources() |
| .size()); |
| |
| List<T> results = new ArrayList<>(); |
| |
| for (T resource : filterResp.getResources()) { |
| EntityTag etag = null; |
| |
| try { |
| etag = etagGenerator.generateEtag(resource); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) { |
| return createETagErrorResponse(); |
| } |
| |
| // Process Attributes |
| try { |
| log.info("=== Calling processFilterAttributeExtensions"); |
| resource = processFilterAttributeExtensions(repository, resource, attributeReferences, excludedAttributeReferences); |
| } catch (ClientFilterException e1) { |
| ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage()); |
| return er.toResponse(); |
| } |
| |
| try { |
| if (!excludedAttributeReferences.isEmpty()) { |
| resource = attributeUtil.setExcludedAttributesForDisplay(resource, excludedAttributeReferences); |
| } else { |
| resource = attributeUtil.setAttributesForDisplay(resource, attributeReferences); |
| } |
| |
| results.add(resource); |
| } catch (AttributeException e) { |
| return createAttributeProcessingErrorResponse(e); |
| } |
| } |
| |
| listResponse.setResources(results); |
| } |
| |
| return Response.ok() |
| .entity(listResponse) |
| .build(); |
| } catch (ScimServerException sse) { |
| LOG.error("Error Processing SCIM Request", sse); |
| return sse.getErrorResponse() |
| .toResponse(); |
| } |
| } |
| |
| @Override |
| public Response update(T resource, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) { |
| try { |
| Repository<T> repository = getRepositoryInternal(); |
| |
| Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| |
| if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) { |
| return createAmbiguousAttributeParametersResponse(); |
| } |
| |
| T stored; |
| try { |
| stored = repository.get(id); |
| } catch (UnableToRetrieveResourceException e2) { |
| log.error("Unable to retrieve resource with id: {}", id, e2); |
| return createGenericExceptionResponse(e2, e2.getStatus()); |
| } catch (Exception e) { |
| log.error("Uncaught repository exception", e); |
| |
| return handleException(e); |
| } |
| |
| if (stored == null) { |
| return createNotFoundResponse(id); |
| } |
| |
| EntityTag backingETag = null; |
| try { |
| backingETag = etagGenerator.generateEtag(stored); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e1) { |
| return createETagErrorResponse(); |
| } |
| |
| ResponseBuilder evaluatePreconditionsResponse = requestContext.getRequest().evaluatePreconditions(backingETag); |
| |
| if (evaluatePreconditionsResponse != null) { |
| return createPreconditionFailedResponse(id, evaluatePreconditionsResponse); |
| } |
| |
| T updated; |
| try { |
| UpdateRequest<T> updateRequest = new UpdateRequest<>(id, stored, resource, schemaRegistry); |
| updated = repository.update(updateRequest); |
| } catch (UnableToUpdateResourceException e1) { |
| return createGenericExceptionResponse(e1, e1.getStatus()); |
| } catch (Exception e1) { |
| log.error("Uncaught repository exception", e1); |
| |
| return handleException(e1); |
| } |
| |
| // Process Attributes |
| try { |
| updated = processFilterAttributeExtensions(repository, updated, attributeReferences, excludedAttributeReferences); |
| } catch (ClientFilterException e1) { |
| ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage()); |
| return er.toResponse(); |
| } |
| |
| try { |
| if (!excludedAttributeReferences.isEmpty()) { |
| updated = attributeUtil.setExcludedAttributesForDisplay(updated, excludedAttributeReferences); |
| } else { |
| updated = attributeUtil.setAttributesForDisplay(updated, attributeReferences); |
| } |
| } catch (AttributeException e) { |
| log.error("Failed to handle attribute processing in update " + e.getMessage()); |
| } |
| |
| EntityTag etag = null; |
| try { |
| etag = etagGenerator.generateEtag(updated); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) { |
| log.error("Failed to generate etag for newly created entity " + e.getMessage()); |
| } |
| |
| // TODO - Is this correct or should we support roll back semantics |
| if (etag == null) { |
| return Response.ok(updated) |
| .location(buildLocationTag(updated)) |
| .build(); |
| } |
| |
| return Response.ok(updated) |
| .location(buildLocationTag(updated)) |
| .tag(etag) |
| .build(); |
| } catch (ScimServerException sse) { |
| LOG.error("Error Processing SCIM Request", sse); |
| return sse.getErrorResponse() |
| .toResponse(); |
| } |
| } |
| |
| @Override |
| public Response patch(PatchRequest patchRequest, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) { |
| try { |
| Repository<T> repository = getRepositoryInternal(); |
| |
| Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes) |
| .map(wrapper -> wrapper.getAttributeReferences()) |
| .orElse(Collections.emptySet()); |
| |
| if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) { |
| return createAmbiguousAttributeParametersResponse(); |
| } |
| |
| T stored; |
| try { |
| stored = repository.get(id); |
| } catch (UnableToRetrieveResourceException e2) { |
| log.error("Unable to retrieve resource with id: {}", id, e2); |
| return createGenericExceptionResponse(e2, e2.getStatus()); |
| } catch (Exception e) { |
| log.error("Uncaught repository exception", e); |
| |
| return handleException(e); |
| } |
| |
| if (stored == null) { |
| return createNotFoundResponse(id); |
| } |
| |
| EntityTag backingETag = null; |
| try { |
| backingETag = etagGenerator.generateEtag(stored); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e1) { |
| return createETagErrorResponse(); |
| } |
| |
| ResponseBuilder evaluatePreconditionsResponse = requestContext.getRequest().evaluatePreconditions(backingETag); |
| |
| if (evaluatePreconditionsResponse != null) { |
| return createPreconditionFailedResponse(id, evaluatePreconditionsResponse); |
| } |
| |
| T updated; |
| try { |
| UpdateRequest<T> updateRequest = new UpdateRequest<>(id, stored, patchRequest.getPatchOperationList(), schemaRegistry); |
| updated = repository.update(updateRequest); |
| } catch (UnableToUpdateResourceException e1) { |
| return createGenericExceptionResponse(e1, e1.getStatus()); |
| } catch (UnsupportedOperationException e2) { |
| return createGenericExceptionResponse(e2, Status.NOT_IMPLEMENTED); |
| } catch (Exception e1) { |
| log.error("Uncaught repository exception", e1); |
| |
| return handleException(e1); |
| } |
| |
| // Process Attributes |
| try { |
| updated = processFilterAttributeExtensions(repository, updated, attributeReferences, excludedAttributeReferences); |
| } catch (ClientFilterException e1) { |
| ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage()); |
| return er.toResponse(); |
| } |
| |
| try { |
| if (!excludedAttributeReferences.isEmpty()) { |
| updated = attributeUtil.setExcludedAttributesForDisplay(updated, excludedAttributeReferences); |
| } else { |
| updated = attributeUtil.setAttributesForDisplay(updated, attributeReferences); |
| } |
| } catch (AttributeException e) { |
| log.error("Failed to handle attribute processing in update " + e.getMessage()); |
| } |
| |
| EntityTag etag = null; |
| try { |
| etag = etagGenerator.generateEtag(updated); |
| } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) { |
| log.error("Failed to generate etag for newly created entity " + e.getMessage()); |
| } |
| |
| // TODO - Is this correct or should we support roll back semantics |
| if (etag == null) { |
| return Response.ok(updated) |
| .location(buildLocationTag(updated)) |
| .build(); |
| } |
| |
| return Response.ok(updated) |
| .location(buildLocationTag(updated)) |
| .tag(etag) |
| .build(); |
| } catch (ScimServerException sse) { |
| LOG.error("Error Processing SCIM Request", sse); |
| return sse.getErrorResponse() |
| .toResponse(); |
| } |
| |
| } |
| |
| @Override |
| public Response delete(String id) { |
| Response response; |
| try { |
| Repository<T> repository = getRepositoryInternal(); |
| |
| try { |
| response = Response.noContent() |
| .build(); |
| |
| repository.delete(id); |
| return response; |
| } catch (UnableToDeleteResourceException e) { |
| response = Response.status(e.getStatus()).build(); |
| log.error("Unable to delete resource", e); |
| |
| return response; |
| } catch (Exception e) { |
| log.error("Uncaught repository exception", e); |
| |
| return handleException(e); |
| } |
| } catch (ScimServerException sse) { |
| LOG.error("Error Processing SCIM Request", sse); |
| return sse.getErrorResponse() |
| .toResponse(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private T processFilterAttributeExtensions(Repository<T> repository, T resource, Set<AttributeReference> attributeReferences, Set<AttributeReference> excludedAttributeReferences) throws ClientFilterException { |
| ScimProcessingExtension annotation = repository.getClass() |
| .getAnnotation(ScimProcessingExtension.class); |
| if (annotation != null) { |
| Class<? extends ProcessingExtension>[] value = annotation.value(); |
| for (Class<? extends ProcessingExtension> class1 : value) { |
| ProcessingExtension processingExtension = CDI.current().select(class1).get(); |
| if (processingExtension instanceof AttributeFilterExtension) { |
| AttributeFilterExtension attributeFilterExtension = (AttributeFilterExtension) processingExtension; |
| ScimRequestContext scimRequestContext = new ScimRequestContext(attributeReferences, excludedAttributeReferences); |
| |
| resource = (T) attributeFilterExtension.filterAttributes(resource, scimRequestContext); |
| log.info("Resource now - " + resource.toString()); |
| } |
| } |
| } |
| |
| return resource; |
| } |
| |
| private URI buildLocationTag(T resource) { |
| String id = resource.getId(); |
| if (id == null) { |
| LOG.warn("Repository must supply an id for a resource"); |
| id = "unknown"; |
| } |
| return requestContext.getUriInfo().getAbsolutePathBuilder() |
| .path(id) |
| .build(); |
| } |
| |
| static Response createGenericExceptionResponse(Throwable e1, int statusCode) { |
| return createGenericExceptionResponse(e1, Status.fromStatusCode(statusCode)); |
| } |
| |
| public static Response createGenericExceptionResponse(Throwable e1, Status status) { |
| Status myStatus = status; |
| if (myStatus == null) { |
| myStatus = Status.INTERNAL_SERVER_ERROR; |
| } |
| |
| ErrorResponse er = new ErrorResponse(myStatus, e1 != null ? e1.getMessage() : "Unknown Server Error"); |
| return er.toResponse(); |
| } |
| |
| private Response createAmbiguousAttributeParametersResponse() { |
| ErrorResponse er = new ErrorResponse(Status.BAD_REQUEST, "Cannot include both attributes and excluded attributes in a single request"); |
| return er.toResponse(); |
| } |
| |
| private Response createNotFoundResponse(String id) { |
| ErrorResponse er = new ErrorResponse(Status.NOT_FOUND, "Resource " + id + " not found"); |
| return er.toResponse(); |
| } |
| |
| private Response createETagErrorResponse() { |
| ErrorResponse er = new ErrorResponse(Status.INTERNAL_SERVER_ERROR, "Failed to generate the etag"); |
| return er.toResponse(); |
| } |
| |
| private Response createAttributeProcessingErrorResponse(Exception e) { |
| ErrorResponse er = new ErrorResponse(Status.INTERNAL_SERVER_ERROR, "Failed to parse the attribute query value " + e.getMessage()); |
| return er.toResponse(); |
| } |
| |
| private Response createNoRepositoryException() { |
| ErrorResponse er = new ErrorResponse(Status.INTERNAL_SERVER_ERROR, "Repository not defined"); |
| return er.toResponse(); |
| } |
| |
| private Response createPreconditionFailedResponse(String id, ResponseBuilder evaluatePreconditionsResponse) { |
| ErrorResponse er = new ErrorResponse(Status.PRECONDITION_FAILED, "Failed to update record, backing record has changed - " + id); |
| log.warn("Failed to update record, backing record has changed - " + id); |
| return evaluatePreconditionsResponse.entity(er) |
| .build(); |
| } |
| |
| Response handleException(Throwable unhandled) { |
| // Allow for ErrorMessageViolationExceptionMapper to handle JAX-RS exceptions by default |
| if (unhandled instanceof WebApplicationException) { |
| throw (WebApplicationException) unhandled; |
| } |
| return BaseResourceTypeResourceImpl.createGenericExceptionResponse(unhandled, Status.INTERNAL_SERVER_ERROR); |
| } |
| } |