/** * Abiquo community edition * cloud management application for hybrid clouds * Copyright (C) 2008-2010 - Abiquo Holdings S.L. * * This application is free software; you can redistribute it and/or * modify it under the terms of the GNU LESSER GENERAL PUBLIC * LICENSE as published by the Free Software Foundation under * version 3 of the License * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * LESSER GENERAL PUBLIC LICENSE v.3 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package com.abiquo.api.web.filters; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.HttpHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import com.abiquo.api.exceptions.APIError; import com.abiquo.api.web.AbiquoHttpServletRequestWrapper; import com.abiquo.model.transport.SingleResourceTransportDto; import com.abiquo.model.transport.error.ErrorsDto; /** * This class intercepts all the requests to the API and injects the proper version parameter to * content-negociation annotations. * * @author jaume */ public class VersionCheckerFilter implements Filter { /** The prefix for Abiquo custom media types. */ private static final String ABIQUO_MIME_TYPE_PREFIX = "application/vnd.abiquo."; /** Register the logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(VersionCheckerFilter.class); /** List of released versions of abiquo API. */ private List<String> releasedVersions; @Override public void init(final FilterConfig config) throws ServletException { releasedVersions = new ArrayList<String>(); releasedVersions.add(SingleResourceTransportDto.API_VERSION); LOGGER.info("VersionCheckerFilter loaded. Current API version is " + SingleResourceTransportDto.API_VERSION); } @Override public void destroy() { LOGGER.info("VersionCheckerFilter destroyed"); } @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { // We need to wrap the request to be able to modify the headers AbiquoHttpServletRequestWrapper req = new AbiquoHttpServletRequestWrapper((HttpServletRequest) request); HttpServletResponse res = (HttpServletResponse) response; try { appendVersionToHeader(req, res, HttpHeaders.ACCEPT); appendVersionToHeader(req, res, HttpHeaders.CONTENT_TYPE); chain.doFilter(req, response); } catch (UnsupportedVersionInHeaderException ex) { flushError(res, ex); return; } } /** * Append the current Abiquo version to the given header if necessary. * * @param request The request. * @param response The response. * @param header The header to parse. * @throws UnsupportedVersionInHeaderException If the header already has a version but it is not * supported by Abiquo. */ private void appendVersionToHeader(final AbiquoHttpServletRequestWrapper request, final HttpServletResponse response, final String header) throws UnsupportedVersionInHeaderException { String mimeType = request.getHeader(header); if (isAbiquoMimeType(mimeType)) { MediaType contentMediaType = MediaType.valueOf(mimeType); String version = contentMediaType.getParameter("version"); if (version == null) { mimeType += ";version=" + SingleResourceTransportDto.API_VERSION; request.setHeader(header, mimeType); } else { if (!releasedVersions.contains(version)) { throw new UnsupportedVersionInHeaderException(header); } } } } /** * Flush the given error to the response. * * @param response The response. * @param exception The error. * @throws IOException If there is an error flushing the error to the output stream. */ private void flushError(final HttpServletResponse response, final UnsupportedVersionInHeaderException exception) throws IOException { String payload = createInvalidVersionNumberXmlError(exception.errorDetails); response.setStatus(exception.errorStatus); response.setContentType(ErrorsDto.MEDIA_TYPE); response.setContentLength(payload.getBytes().length); response.getWriter().print(payload); response.getWriter().flush(); } /** * We are outside WINK! Resource provider for XML is our hands... Invalid version number. * * @return the string with the error. */ private String createInvalidVersionNumberXmlError(final APIError error) { StringBuilder builder = new StringBuilder(); builder.append("<errors>"); builder.append("<error>"); builder.append("<code>").append(error.getCode()).append("</code>"); builder.append("<message>").append(error.getMessage()).append("</message>"); builder.append("</error>"); builder.append("</errors>"); return builder.toString(); } /** * Check if the given mime type is a custom Abiquo mime type. * * @param mimeType The mime type to check. * @return Boolean indicating if the given mime type is a custom Abiquo mime type. */ private static boolean isAbiquoMimeType(final String mimeType) { return mimeType != null && mimeType.startsWith(ABIQUO_MIME_TYPE_PREFIX); } /** * Encapsulates invalid version error details. */ private static class UnsupportedVersionInHeaderException extends Exception { private static final long serialVersionUID = 1L; /** The response error status code. */ private int errorStatus; /** The details of the error. */ private APIError errorDetails; public UnsupportedVersionInHeaderException(final String header) { if (header.equals(HttpHeaders.ACCEPT)) { errorStatus = HttpServletResponse.SC_NOT_ACCEPTABLE; errorDetails = APIError.STATUS_NOT_ACCEPTABLE_VERSION; } else if (header.equals(HttpHeaders.CONTENT_TYPE)) { errorStatus = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; errorDetails = APIError.STATUS_UNSUPPORTED_MEDIA_TYPE_VERSION; } else { throw new IllegalArgumentException("Unsupported header: " + header); } } } }