/* * Copyright 2012 Research Studios Austria Forschungsges.m.b.H. * * 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 won.protocol.rest; import org.hibernate.annotations.common.util.impl.LoggerFactory; import org.jboss.logging.Logger; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import java.util.List; /** * Utils for our very specific way of creating/parsing etags. */ public class WonEtagHelper { private static final Logger logger = LoggerFactory.logger(WonEtagHelper.class); private static final char VERSION_MEDIATYPE_DELIMITER=' '; private String version = null; private MediaType mediaType = null; private WonEtagHelper(final String version, final MediaType mediaType) { this.version = version; this.mediaType = mediaType; } public static WonEtagHelper forVersion(String version){ if (version == null) return null; return new WonEtagHelper(version, null); } /** * Returns a WonEtagHelper if the specified string is a valid ETAG, combining * [VERSION] {[DELIMITER] [MEDIA-TYPE]} (curly brackets inticate 'optional') * Returns null if the specified etagValue is null or not valid. * @param etagValue * @return */ public static WonEtagHelper fromEtagValue(String etagValue){ if (etagValue == null) return null; etagValue = etagValue.trim(); if (etagValue.startsWith("W/")) { logger.debug("weak etag matching is not supported, cannot " + "process: " +etagValue); return null; } if (!etagValue.startsWith("\"")) { logger.debug("etag must start with '\"', cannot process: " +etagValue); return null; } if (!etagValue.endsWith("\"")) { logger.debug("etag must end with '\"', cannot process: " +etagValue); return null; } int index = etagValue.indexOf(VERSION_MEDIATYPE_DELIMITER); if (index == -1){ //delimiter not found. Assume only version return new WonEtagHelper(etagValue.substring(1,etagValue.length()-1), null); } MediaType mt = null; try { mt = MediaType.parseMediaType(etagValue.substring(index,etagValue.length()-1)); } catch (Exception e){ logger.debug("not a valid media type in etag value, cannot process: " + etagValue); //not a valid media type return null; } return new WonEtagHelper(etagValue.substring(1,index), mt); } /** * Returns a new WonEtagHelper object created from the specified headers if there is an ETAG header and the value * parsed for the mediatype is actually compatible with the mediatypes listed in the accept header. We assume that * if that is not the case, the server will have to produce a new result as the mediatype cached by the client is * different from the one the server will produce now. * If there is no Accept header, the helper object will be returned. * If parsing the ETAG does not reveal a MediaType, the helperObject will not be returned. * @param headers * @return */ public static WonEtagHelper fromHeaderIfCompatibleWithAcceptHeader(HttpHeaders headers){ WonEtagHelper helper = fromIfNoneMatchHeader(headers); if (helper == null || helper.getMediaType() == null) { return null; } if (headers.getAccept().stream().anyMatch( m -> m.isCompatibleWith(helper.getMediaType()))){ return helper; } return null; } public static WonEtagHelper fromIfNoneMatchHeader(HttpHeaders headers){ List<String> etags = headers.getIfNoneMatch(); if (etags.size() == 0) return null; if (etags.size() > 1) { logger.info("found more than one If-None-Match header values, only using first one"); }; return fromEtagValue(etags.get(0)); } public static WonEtagHelper fromEtagHeader(HttpHeaders headers){ return fromEtagValue(headers.getETag()); } public static void setMediaTypeForEtagHeaderIfPresent(MediaType mediaType, HttpHeaders headers){ WonEtagHelper wonEtagHelper = fromEtagHeader(headers); if (wonEtagHelper != null){ wonEtagHelper.setMediaType(mediaType); setEtagHeader(wonEtagHelper, headers); } } public static void setEtagHeader(WonEtagHelper wonEtagHelper, HttpHeaders headers){ if (wonEtagHelper.isValid()) { headers.setETag(wonEtagHelper.getEtagString()); } } public static String getVersionIdentifier(WonEtagHelper wonEtagHelper){ if (wonEtagHelper != null){ return wonEtagHelper.getVersion(); } return null; } public void setVersion(final String version) { this.version = version; } public void setMediaType(final MediaType mediaType) { this.mediaType = mediaType; } public String getVersion() { return version; } public MediaType getMediaType() { return mediaType; } public boolean isValid() { return version != null; } public String getEtagString(){ StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('"').append(version); if (mediaType != null){ stringBuilder.append(VERSION_MEDIATYPE_DELIMITER); stringBuilder.append(mediaType.toString()); } stringBuilder.append('"'); return stringBuilder.toString(); } }