/* * Copyright 2016 the original author or authors. * * Licensed 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.springframework.data.rest.webmvc; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; import java.util.List; import java.util.function.Supplier; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.rest.webmvc.support.ETag; import org.springframework.hateoas.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; /** * Simple abstraction to capture the status of a resource to determine whether it has been modified or not and produce * {@link ResponseEntity} via its {@link StatusAndHeaders} sub component. * * @author Oliver Gierke * @since 2.6 * @soundtrack Blumentopf - Dass ich nicht lache (Kein Zufall) */ @RequiredArgsConstructor(staticName = "of") class ResourceStatus { private final @NonNull HttpHeadersPreparer preparer; /** * Returns the {@link StatusAndHeaders} calculated from the given {@link HttpHeaders}, domain object and * {@link RootResourceInformation. * * @param requestHeaders must not be {@literal null}. * @param domainObject must not be {@literal null}. * @param information * @return */ public StatusAndHeaders getStatusAndHeaders(HttpHeaders requestHeaders, Object domainObject, PersistentEntity<?, ?> entity) { // Check ETag for If-Non-Match List<String> ifNoneMatch = requestHeaders.getIfNoneMatch(); ETag eTag = ifNoneMatch.isEmpty() ? ETag.NO_ETAG : ETag.from(ifNoneMatch.get(0)); HttpHeaders responseHeaders = preparer.prepareHeaders(entity, domainObject); // Check last modification for If-Modified-Since return eTag.matches(entity, domainObject) || preparer.isObjectStillValid(domainObject, requestHeaders) ? StatusAndHeaders.notModified(responseHeaders) : StatusAndHeaders.modified(responseHeaders); } @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public static class StatusAndHeaders { private final @NonNull HttpHeaders headers; private final @Getter(AccessLevel.PACKAGE) boolean modified; private static StatusAndHeaders notModified(HttpHeaders headers) { return new StatusAndHeaders(headers, false); } private static StatusAndHeaders modified(HttpHeaders headers) { return new StatusAndHeaders(headers, true); } /** * Creates a {@link ResponseEntity} based on the given {@link PersistentEntityResource}. * * @param supplier a {@link Supplier} to provide a {@link PersistentEntityResource} eventually, must not be * {@literal null}. * @return */ public ResponseEntity<Resource<?>> toResponseEntity(Supplier<PersistentEntityResource> supplier) { return modified ? new ResponseEntity<Resource<?>>(supplier.get(), headers, HttpStatus.OK) : new ResponseEntity<Resource<?>>(headers, HttpStatus.NOT_MODIFIED); } } }