ch.entwine.weblounge.contentrepository.impl.endpoint.FilesEndpoint.java Source code

Java tutorial

Introduction

Here is the source code for ch.entwine.weblounge.contentrepository.impl.endpoint.FilesEndpoint.java

Source

/*
 *  Weblounge: Web Content Management System
 *  Copyright (c) 2003 - 2011 The Weblounge Team
 *  http://entwinemedia.com/weblounge
 *
 *  This program 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; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program 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 for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software Foundation
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package ch.entwine.weblounge.contentrepository.impl.endpoint;

import ch.entwine.weblounge.common.content.Resource;
import ch.entwine.weblounge.common.content.ResourceContent;
import ch.entwine.weblounge.common.content.ResourceContentReader;
import ch.entwine.weblounge.common.content.ResourceReader;
import ch.entwine.weblounge.common.content.ResourceSearchResultItem;
import ch.entwine.weblounge.common.content.ResourceURI;
import ch.entwine.weblounge.common.content.ResourceUtils;
import ch.entwine.weblounge.common.content.SearchQuery;
import ch.entwine.weblounge.common.content.SearchQuery.Order;
import ch.entwine.weblounge.common.content.SearchResult;
import ch.entwine.weblounge.common.content.SearchResultItem;
import ch.entwine.weblounge.common.content.file.FileResource;
import ch.entwine.weblounge.common.content.page.Page;
import ch.entwine.weblounge.common.impl.content.GeneralResourceURIImpl;
import ch.entwine.weblounge.common.impl.content.ResourceURIImpl;
import ch.entwine.weblounge.common.impl.content.SearchQueryImpl;
import ch.entwine.weblounge.common.impl.content.file.FileResourceImpl;
import ch.entwine.weblounge.common.impl.content.page.PageSearchResultItemImpl;
import ch.entwine.weblounge.common.impl.language.LanguageUtils;
import ch.entwine.weblounge.common.impl.security.SecurityUtils;
import ch.entwine.weblounge.common.impl.security.SystemRole;
import ch.entwine.weblounge.common.impl.security.UserImpl;
import ch.entwine.weblounge.common.impl.url.WebUrlImpl;
import ch.entwine.weblounge.common.language.Language;
import ch.entwine.weblounge.common.language.UnknownLanguageException;
import ch.entwine.weblounge.common.repository.ContentRepository;
import ch.entwine.weblounge.common.repository.ContentRepositoryException;
import ch.entwine.weblounge.common.repository.ReferentialIntegrityException;
import ch.entwine.weblounge.common.repository.ResourceSerializer;
import ch.entwine.weblounge.common.repository.ResourceSerializerService;
import ch.entwine.weblounge.common.repository.WritableContentRepository;
import ch.entwine.weblounge.common.security.SecurityService;
import ch.entwine.weblounge.common.security.User;
import ch.entwine.weblounge.common.site.Site;
import ch.entwine.weblounge.common.url.UrlUtils;
import ch.entwine.weblounge.common.url.WebUrl;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tika.Tika;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Calendar;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.xml.parsers.ParserConfigurationException;

/**
 * This class implements the <code>REST</code> endpoint for file data.
 */
@Path("/")
@Produces(MediaType.TEXT_XML)
public class FilesEndpoint extends ContentRepositoryEndpoint {

    /** Request parameter name for the path */
    public static final String OPT_PATH = "path";

    /** Request parameter name for the content language */
    public static final String OPT_LANGUAGE = "language";

    /** Request parameter name for the content type */
    public static final String OPT_MIMETYPE = "mimeType";

    /** Logging facility */
    private static final Logger logger = LoggerFactory.getLogger(FilesEndpoint.class);

    /** Mime type detector */
    private final Tika mimeTypeDetector = new Tika();

    /** The security service */
    protected SecurityService securityService = null;

    /** The resource serializer service */
    private ResourceSerializerService serializerService = null;

    /** The endpoint documentation */
    private String docs = null;

    /**
     * Returns a collection of files which match the given criteria.
     * 
     * @param request
     *          the request
     * @param path
     *          the file path (e.g. <code>/my/simple/path</code>)
     * @param subjectstring
     *          one ore more subjects, divided by a comma
     * @param searchterms
     *          fulltext search terms
     * @param filter
     *          further search result filtering
     * @param type
     *          the file type, e. g.
     *          {@link ch.entwine.weblounge.common.content.image.ImageResource#TYPE}
     * @param sort
     *          sort order, possible values are
     *          <code>created-asc, created-desc, published-asc, published-desc, modified-asc & modified-desc</code>
     * @param limit
     *          search result limit
     * @param offset
     *          search result offset (for paging in combination with limit)
     * @return a collection of matching files
     */
    @GET
    @Path("/")
    public Response getAllFiles(@Context HttpServletRequest request, @QueryParam("path") String path,
            @QueryParam("subjects") String subjectstring, @QueryParam("searchterms") String searchterms,
            @QueryParam("filter") String filter, @QueryParam("type") String type,
            @QueryParam("sort") @DefaultValue("modified-desc") String sort,
            @QueryParam("limit") @DefaultValue("10") int limit,
            @QueryParam("offset") @DefaultValue("0") int offset) {

        // Create search query
        Site site = getSite(request);
        SearchQuery q = new SearchQueryImpl(site);
        q.withVersion(Resource.LIVE);

        // Type
        q.withoutTypes(Page.TYPE);
        if (StringUtils.isNotBlank(type))
            q.withTypes(type);

        // Path
        if (StringUtils.isNotBlank(path))
            q.withPath(path);

        // Subjects
        if (StringUtils.isNotBlank(subjectstring)) {
            StringTokenizer subjects = new StringTokenizer(subjectstring, ",");
            while (subjects.hasMoreTokens())
                q.withSubject(subjects.nextToken());
        }

        // Search terms
        if (StringUtils.isNotBlank(searchterms))
            q.withText(true, searchterms);

        Calendar today = Calendar.getInstance();
        today.set(Calendar.HOUR_OF_DAY, 0);
        today.set(Calendar.MINUTE, 0);
        today.set(Calendar.SECOND, 0);
        today.set(Calendar.MILLISECOND, 0);
        Calendar yesterday = Calendar.getInstance();
        yesterday.add(Calendar.DATE, -1);
        yesterday.set(Calendar.HOUR_OF_DAY, 0);
        yesterday.set(Calendar.MINUTE, 0);
        yesterday.set(Calendar.SECOND, 0);
        yesterday.set(Calendar.MILLISECOND, 0);
        Calendar tomorrow = Calendar.getInstance();
        tomorrow.add(Calendar.DATE, 1);
        tomorrow.set(Calendar.HOUR_OF_DAY, 0);
        tomorrow.set(Calendar.MINUTE, 0);
        tomorrow.set(Calendar.SECOND, 0);
        tomorrow.set(Calendar.MILLISECOND, 0);

        // Filter query
        if (StringUtils.isNotBlank(filter)) {
            if ("/".equals(filter)) {
                q.withPath("/");
            }

            // by user
            else if (filter.startsWith("creator:") && filter.length() > "creator:".length()) {
                String creator = StringUtils.trim(filter.substring("creator:".length()));
                if ("me".equals(creator))
                    q.withCreator(securityService.getUser());
                else
                    q.withCreator(new UserImpl(creator));
            } else if (filter.startsWith("modifier:") && filter.length() > "modifier:".length()) {
                String modifier = StringUtils.trim(filter.substring("modifier:".length()));
                if ("me".equals(modifier))
                    q.withModifier(securityService.getUser());
                else
                    q.withModifier(new UserImpl(modifier));
            } else if (filter.startsWith("publisher:") && filter.length() > "publisher:".length()) {
                String publisher = StringUtils.trim(filter.substring("publisher:".length()));
                if ("me".equals(publisher))
                    q.withPublisher(securityService.getUser());
                else
                    q.withPublisher(new UserImpl(publisher));
            }

            // by date

            else if (filter.startsWith("created:") && filter.length() > "created:".length()) {
                String created = StringUtils.trim(filter.substring("created:".length()));
                if ("today".equals(created))
                    q.withCreationDateBetween(today.getTime()).and(tomorrow.getTime());
                else if ("yesterday".equals(created))
                    q.withCreationDateBetween(yesterday.getTime()).and(today.getTime());
                else
                    q.withCreationDate(tomorrow.getTime());
            } else if (filter.startsWith("modified:") && filter.length() > "modified:".length()) {
                String modified = StringUtils.trim(filter.substring("modified:".length()));
                if ("today".equals(modified))
                    q.withModificationDateBetween(today.getTime()).and(tomorrow.getTime());
                else if ("yesterday".equals(modified))
                    q.withModificationDateBetween(yesterday.getTime()).and(today.getTime());
                else
                    q.withCreationDate(tomorrow.getTime());
            } else if (filter.startsWith("publisher:") && filter.length() > "publisher:".length()) {
                String published = StringUtils.trim(filter.substring("published:".length()));
                if ("today".equals(published))
                    q.withPublishingDateBetween(today.getTime()).and(tomorrow.getTime());
                else if ("yesterday".equals(published))
                    q.withPublishingDateBetween(yesterday.getTime()).and(today.getTime());
                else
                    q.withCreationDate(tomorrow.getTime());
            }

            // by id
            else if (filter.contains("id:")) {
                String[] searchTerms = StringUtils.split(filter);
                for (String searchTerm : searchTerms) {
                    if (searchTerm.startsWith("id:") && filter.length() > "id:".length()) {
                        q.withIdentifier(StringUtils.trim(searchTerm.substring("id:".length())));
                    }
                }
            }

            // simple filter
            else {
                q.withFulltext(true, filter);
            }
        }

        // Limit and Offset
        q.withLimit(limit);
        q.withOffset(offset);

        // Sort order
        if (StringUtils.equalsIgnoreCase("modified-asc", sort)) {
            q.sortByModificationDate(Order.Ascending);
        } else if (StringUtils.equalsIgnoreCase("modified-desc", sort)) {
            q.sortByModificationDate(Order.Descending);
        } else if (StringUtils.equalsIgnoreCase("created-asc", sort)) {
            q.sortByCreationDate(Order.Ascending);
        } else if (StringUtils.equalsIgnoreCase("created-desc", sort)) {
            q.sortByCreationDate(Order.Descending);
        } else if (StringUtils.equalsIgnoreCase("published-asc", sort)) {
            q.sortByPublishingDate(Order.Ascending);
        } else if (StringUtils.equalsIgnoreCase("published-desc", sort)) {
            q.sortByPublishingDate(Order.Descending);
        }

        // Load the result
        String result = loadResultSet(q);

        // Return the response
        return Response.ok(result).build();
    }

    /**
     * Returns a collection of files that are defined as pending.
     * 
     * @param request
     *          the request
     * @param filter
     *          further search result filtering
     * @param type
     *          the file type, e. g.
     *          {@link ch.entwine.weblounge.common.content.image.ImageResource#TYPE}
     * @param sort
     *          sort order, possible values are
     *          <code>created-asc, created-desc, published-asc, published-desc, modified-asc & modified-desc</code>
     * @param limit
     *          search result limit
     * @param offset
     *          search result offset (for paging in combination with limit)
     * @return a collection of matching files
     */
    @GET
    @Path("/pending")
    public Response getPending(@Context HttpServletRequest request, @QueryParam("filter") String filter,
            @QueryParam("type") String type, @QueryParam("sort") @DefaultValue("modified-desc") String sort,
            @QueryParam("limit") @DefaultValue("10") int limit,
            @QueryParam("offset") @DefaultValue("0") int offset) {

        // Create search query
        Site site = getSite(request);
        SearchQuery q = new SearchQueryImpl(site);
        q.withVersion(Resource.LIVE);

        // Only take resources that have not been modified
        q.withoutModification();

        // Type
        q.withoutTypes(Page.TYPE);
        if (StringUtils.isNotBlank(type))
            q.withTypes(type);

        // Filter query
        if (StringUtils.isNotBlank(filter))
            q.withFilter(filter);

        // Limit and Offset
        q.withLimit(limit);
        q.withOffset(offset);

        // Sort order
        if (StringUtils.equalsIgnoreCase("modified-asc", sort)) {
            q.sortByModificationDate(Order.Ascending);
        } else if (StringUtils.equalsIgnoreCase("modified-desc", sort)) {
            q.sortByModificationDate(Order.Descending);
        } else if (StringUtils.equalsIgnoreCase("created-asc", sort)) {
            q.sortByCreationDate(Order.Ascending);
        } else if (StringUtils.equalsIgnoreCase("created-desc", sort)) {
            q.sortByCreationDate(Order.Descending);
        } else if (StringUtils.equalsIgnoreCase("published-asc", sort)) {
            q.sortByPublishingDate(Order.Ascending);
        } else if (StringUtils.equalsIgnoreCase("published-desc", sort)) {
            q.sortByPublishingDate(Order.Descending);
        }

        // Load the result
        String result = loadResultSet(q);

        // Return the response
        return Response.ok(result).build();

    }

    /**
     * Returns the resource with the given identifier or a <code>404</code> if the
     * resource could not be found.
     * 
     * @param request
     *          the request
     * @param resourceId
     *          the resource identifier
     * @return the resource
     */
    @GET
    @Produces("text/xml")
    @Path("/{resource}")
    public Response getFileById(@Context HttpServletRequest request, @PathParam("resource") String resourceId) {

        // Check the parameters
        if (resourceId == null)
            throw new WebApplicationException(Status.BAD_REQUEST);

        // Get the resource
        Resource<?> resource = loadResource(request, resourceId, null);
        if (resource == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        // Is there an up-to-date, cached version on the client side?
        if (!ResourceUtils.hasChanged(request, resource)) {
            return Response.notModified().build();
        }

        // Create the response
        ResponseBuilder response = Response.ok(resource.toXml());
        response.tag(ResourceUtils.getETagValue(resource));
        response.lastModified(ResourceUtils.getModificationDate(resource));
        return response.build();
    }

    /**
     * Returns pages containing pagelets with properties of name
     * <code>resourceid</code> and a value equal to that of the resource
     * identifier.
     * 
     * @param request
     *          the request
     * @param resourceId
     *          the resource identifier
     * @return the referring pages
     */
    @GET
    @Path("/{resource}/referrer")
    public Response getReferencesByURI(@Context HttpServletRequest request,
            @PathParam("resource") String resourceId) {

        // Check the parameters
        if (resourceId == null)
            return Response.status(Status.BAD_REQUEST).build();

        Site site = getSite(request);
        SearchQuery q = new SearchQueryImpl(site);
        q.withVersion(Resource.LIVE);
        q.withTypes(Page.TYPE);
        q.withProperty("resourceid", resourceId);

        ContentRepository repository = getContentRepository(site, false);
        SearchResult result = null;
        try {
            result = repository.find(q);
        } catch (ContentRepositoryException e) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        StringBuffer buf = new StringBuffer("<pages>");
        for (SearchResultItem item : result.getItems()) {
            if (resourceId.equals(item.getId()))
                continue;
            String headerXml = ((PageSearchResultItemImpl) item).getPageHeaderXml();
            buf.append(headerXml);
        }
        buf.append("</pages>");

        // Create the response
        return Response.ok(buf.toString()).build();
    }

    /**
     * Returns the resource content with the given identifier or a
     * <code>404</code> if the resource or the resource content could not be
     * found.
     * 
     * @param request
     *          the request
     * @param resourceId
     *          the resource identifier
     * @param language
     *          the language identifier
     * @return the resource
     */
    @GET
    @Path("/{resource}/content/{language}")
    public Response getFileContent(@Context HttpServletRequest request, @PathParam("resource") String resourceId,
            @PathParam("language") String languageId) {

        // Check the parameters
        if (resourceId == null)
            throw new WebApplicationException(Status.BAD_REQUEST);

        // Extract the language
        Language language;
        try {
            language = LanguageUtils.getLanguage(languageId);
        } catch (UnknownLanguageException e) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }
        if (language == null)
            throw new WebApplicationException(Status.NOT_FOUND);

        // Get the resource
        Resource<?> resource = loadResource(request, resourceId, null);
        if (resource == null || resource.contents().isEmpty()) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        return getResourceContent(request, resource, language);
    }

    /**
     * Adds the resource content with language <code>language</code> to the
     * specified resource.
     * 
     * @param request
     *          the request
     * @param resourceId
     *          the resource identifier
     * @param languageId
     *          the language identifier
     * @param is
     *          the input stream
     * @return the resource
     */
    @POST
    @Path("/{resource}/content/{language}")
    @Produces(MediaType.MEDIA_TYPE_WILDCARD)
    public Response addFileContent(@Context HttpServletRequest request, @PathParam("resource") String resourceId,
            @PathParam("language") String languageId) {

        Site site = getSite(request);

        // Check the parameters
        if (resourceId == null)
            throw new WebApplicationException(Status.BAD_REQUEST);

        // Extract the language
        Language language = LanguageUtils.getLanguage(languageId);
        if (language == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        // Get the resource
        Resource<?> resource = loadResource(request, resourceId, null);
        if (resource == null || resource.contents().isEmpty()) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        String fileName = null;
        String mimeType = null;
        File uploadedFile = null;

        try {
            // Multipart form encoding?
            if (ServletFileUpload.isMultipartContent(request)) {
                try {
                    ServletFileUpload payload = new ServletFileUpload();
                    for (FileItemIterator iter = payload.getItemIterator(request); iter.hasNext();) {
                        FileItemStream item = iter.next();
                        if (item.isFormField()) {
                            String fieldName = item.getFieldName();
                            String fieldValue = Streams.asString(item.openStream());
                            if (StringUtils.isBlank(fieldValue))
                                continue;
                            if (OPT_MIMETYPE.equals(fieldName)) {
                                mimeType = fieldValue;
                            }
                        } else {
                            // once the body gets read iter.hasNext must not be invoked
                            // or the stream can not be read
                            fileName = StringUtils.trim(item.getName());
                            mimeType = StringUtils.trim(item.getContentType());
                            uploadedFile = File.createTempFile("upload-", null);
                            FileOutputStream fos = new FileOutputStream(uploadedFile);
                            try {
                                IOUtils.copy(item.openStream(), fos);
                            } catch (IOException e) {
                                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                            } finally {
                                IOUtils.closeQuietly(fos);
                            }
                        }
                    }
                } catch (FileUploadException e) {
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                } catch (IOException e) {
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                }
            }

            // Octet binary stream
            else {
                try {
                    fileName = StringUtils.trimToNull(request.getHeader("X-File-Name"));
                    mimeType = StringUtils.trimToNull(request.getParameter(OPT_MIMETYPE));
                } catch (UnknownLanguageException e) {
                    throw new WebApplicationException(Status.BAD_REQUEST);
                }
                InputStream is = null;
                FileOutputStream fos = null;
                try {
                    is = request.getInputStream();
                    if (is == null)
                        throw new WebApplicationException(Status.BAD_REQUEST);
                    uploadedFile = File.createTempFile("upload-", null);
                    fos = new FileOutputStream(uploadedFile);
                    IOUtils.copy(is, fos);
                } catch (IOException e) {
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                } finally {
                    IOUtils.closeQuietly(is);
                    IOUtils.closeQuietly(fos);
                }

            }
            // Has there been a file in the request?
            if (uploadedFile == null)
                throw new WebApplicationException(Status.BAD_REQUEST);

            // A mime type would be nice as well
            if (StringUtils.isBlank(mimeType)) {
                mimeType = detectMimeTypeFromFile(fileName, uploadedFile);
                if (mimeType == null)
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            }

            // Get the current user
            User user = securityService.getUser();
            if (user == null)
                throw new WebApplicationException(Status.UNAUTHORIZED);

            // Make sure the user has editing rights
            if (!SecurityUtils.userHasRole(user, SystemRole.EDITOR))
                throw new WebApplicationException(Status.UNAUTHORIZED);

            // Try to create the resource content
            InputStream is = null;
            ResourceContent content = null;
            ResourceContentReader<?> reader = null;
            ResourceSerializer<?, ?> serializer = serializerService
                    .getSerializerByType(resource.getURI().getType());
            try {
                reader = serializer.getContentReader();
                is = new FileInputStream(uploadedFile);
                content = reader.createFromContent(is, user, language, uploadedFile.length(), fileName, mimeType);
            } catch (IOException e) {
                logger.warn("Error reading resource content {} from request", resource.getURI());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (ParserConfigurationException e) {
                logger.warn("Error configuring parser to read resource content {}: {}", resource.getURI(),
                        e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (SAXException e) {
                logger.warn("Error parsing udpated resource {}: {}", resource.getURI(), e.getMessage());
                throw new WebApplicationException(Status.BAD_REQUEST);
            } finally {
                IOUtils.closeQuietly(is);
            }

            URI uri = null;
            WritableContentRepository contentRepository = (WritableContentRepository) getContentRepository(site,
                    true);
            try {
                is = new FileInputStream(uploadedFile);
                resource = contentRepository.putContent(resource.getURI(), content, is);
                uri = new URI(resource.getURI().getIdentifier());
            } catch (IOException e) {
                logger.warn("Error writing content to resource {}: {}", resource.getURI(), e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (IllegalStateException e) {
                logger.warn("Illegal state while adding content to resource {}: {}", resource.getURI(),
                        e.getMessage());
                throw new WebApplicationException(Status.PRECONDITION_FAILED);
            } catch (ContentRepositoryException e) {
                logger.warn("Error adding content to resource {}: {}", resource.getURI(), e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (URISyntaxException e) {
                logger.warn("Error creating a uri for resource {}: {}", resource.getURI(), e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } finally {
                IOUtils.closeQuietly(is);
            }

            // Create the response
            ResponseBuilder response = Response.created(uri);
            response.type(MediaType.MEDIA_TYPE_WILDCARD);
            response.tag(ResourceUtils.getETagValue(resource));
            response.lastModified(ResourceUtils.getModificationDate(resource, language));
            return response.build();

        } finally {
            FileUtils.deleteQuietly(uploadedFile);
        }
    }

    /**
     * Returns the resource content with the given identifier or a
     * <code>404</code> if the resource or the resource content could not be
     * found.
     * 
     * @param request
     *          the request
     * @param resourceId
     *          the resource identifier
     * @param languageId
     *          the language identifier
     * @return the resource
     */
    @DELETE
    @Path("/{resource}/content/{language}")
    public Response deleteFileContent(@Context HttpServletRequest request, @PathParam("resource") String resourceId,
            @PathParam("language") String languageId) {

        // Check the parameters
        if (resourceId == null)
            throw new WebApplicationException(Status.BAD_REQUEST);

        // Extract the language
        Language language = LanguageUtils.getLanguage(languageId);
        if (language == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        // Get the resource
        Resource<?> resource = loadResource(request, resourceId, null);
        if (resource == null || resource.contents().isEmpty()) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        // Get the resource content
        ResourceContent content = resource.getContent(language);
        if (content == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }

        ResourceURI uri = resource.getURI();
        Site site = getSite(request);

        // Get the current user
        User user = securityService.getUser();
        if (user == null)
            throw new WebApplicationException(Status.UNAUTHORIZED);

        // Make sure the user has editing rights
        if (!SecurityUtils.userHasRole(user, SystemRole.EDITOR))
            throw new WebApplicationException(Status.UNAUTHORIZED);

        WritableContentRepository contentRepository = (WritableContentRepository) getContentRepository(site, true);

        // Delete the resource
        try {
            resource = contentRepository.deleteContent(uri, content);
            resource.setModified(user, new Date());
            // TODO: Remove existing preview images
            contentRepository.put(resource);
        } catch (IllegalStateException e) {
            logger.warn("Tried to remove content from missing resource " + uri);
            throw new WebApplicationException(Status.NOT_FOUND);
        } catch (ContentRepositoryException e) {
            logger.warn("Error while accessing resource " + uri);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (IOException e) {
            logger.warn("Error while deleting content from resource " + uri);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        // Create the response
        ResponseBuilder response = Response.ok(resource.toXml());
        response.tag(ResourceUtils.getETagValue(resource));
        response.lastModified(ResourceUtils.getModificationDate(resource));
        return response.build();
    }

    /**
     * Updates the indicated resource.
     * 
     * @param request
     *          the http request
     * @param resourceId
     *          the resource identifier
     * @param ifMatchHeader
     *          the resource's <code>etag</code> value
     * @param resourceContent
     *          the resource content
     * @return response an empty response
     * @throws WebApplicationException
     *           if the update fails
     */
    @PUT
    @Path("/{resource}")
    public Response updateFile(@Context HttpServletRequest request, @PathParam("resource") String resourceId,
            @FormParam("content") String resourceXml, @HeaderParam("If-Match") String ifMatchHeader) {

        // Check the parameters
        if (resourceId == null)
            return Response.status(Status.BAD_REQUEST).build();
        if (resourceXml == null)
            return Response.status(Status.BAD_REQUEST).build();

        // Extract the site
        Site site = getSite(request);

        // Make sure the content repository is writable
        if (site.getContentRepository().isReadOnly()) {
            logger.warn("Attempt to write to read-only content repository {}", site);
            throw new WebApplicationException(Status.PRECONDITION_FAILED);
        }

        WritableContentRepository contentRepository = (WritableContentRepository) getContentRepository(site, true);
        ResourceURI resourceURI = null;

        // Does the resource exist?
        try {
            resourceURI = contentRepository.getResourceURI(resourceId);
            if (resourceURI == null) {
                throw new WebApplicationException(Status.NOT_FOUND);
            }
        } catch (ContentRepositoryException e) {
            logger.warn("Error lookup up resource {} from repository: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        Resource<?> currentResource;
        try {
            currentResource = contentRepository.get(resourceURI);
        } catch (ContentRepositoryException e) {
            logger.warn("Error reading current resource {} from repository: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        // Check the value of the If-Match header against the etag
        if (ifMatchHeader != null) {
            String etag = Long.toString(ResourceUtils.getModificationDate(currentResource).getTime());
            if (!etag.equals(ifMatchHeader)) {
                throw new WebApplicationException(Status.PRECONDITION_FAILED);
            }
        }

        // Get the current user
        User user = securityService.getUser();
        if (user == null)
            throw new WebApplicationException(Status.UNAUTHORIZED);

        // Make sure the user has editing rights
        if (!SecurityUtils.userHasRole(user, SystemRole.EDITOR))
            throw new WebApplicationException(Status.UNAUTHORIZED);

        // Parse the resource and update it in the repository
        Resource<?> resource = null;
        // TOOD: Extract resource type
        String resourceType = resourceURI.getType();
        try {
            ResourceSerializer<?, ?> serializer = serializerService.getSerializerByType(resourceType);
            ResourceReader<?, ?> resourceReader = serializer.getReader();
            resource = resourceReader.read(IOUtils.toInputStream(resourceXml, "utf-8"), site);
            resource.setModified(user, new Date());
            contentRepository.put(resource, false);

            // Check if the resource has been moved
            String currentPath = currentResource.getURI().getPath();
            String newPath = StringUtils.trimToNull(resource.getURI().getPath());
            if (currentPath != null && newPath != null && !currentPath.equals(newPath)) {
                contentRepository.move(currentResource.getURI(), newPath, true);
            }
        } catch (IOException e) {
            logger.warn("Error reading udpated resource {} from request", resourceURI);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (ParserConfigurationException e) {
            logger.warn("Error configuring parser to read udpated resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (SAXException e) {
            logger.warn("Error parsing udpated resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.BAD_REQUEST);
        } catch (IllegalStateException e) {
            logger.warn("Illegal state while udpating resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.PRECONDITION_FAILED);
        } catch (ContentRepositoryException e) {
            logger.warn("Error udpating resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        // Create the response
        ResponseBuilder response = Response.ok();
        response.tag(ResourceUtils.getETagValue(resource));
        response.lastModified(ResourceUtils.getModificationDate(resource));
        return response.build();
    }

    /**
     * Creates a file resource at the site's content repository and returns the
     * location to post updates to.
     * 
     * @param request
     *          the http request
     * @param resourceXml
     *          the new resource
     * @param path
     *          the path to store the resource at
     * @return response the resource location
     */
    @POST
    @Path("/")
    public Response createFile(@Context HttpServletRequest request, @FormParam("path") String path) {

        Site site = getSite(request);
        WritableContentRepository contentRepository = (WritableContentRepository) getContentRepository(site, true);

        // Get the current user
        User user = securityService.getUser();
        if (user == null)
            throw new WebApplicationException(Status.UNAUTHORIZED);

        // Make sure the user has editing rights
        if (!SecurityUtils.userHasRole(user, SystemRole.EDITOR))
            throw new WebApplicationException(Status.UNAUTHORIZED);

        // Create the resource uri
        ResourceURIImpl resourceURI = null;
        String uuid = UUID.randomUUID().toString();
        if (!StringUtils.isBlank(path)) {
            try {
                if (!path.startsWith("/"))
                    path = "/" + path;
                WebUrl url = new WebUrlImpl(site, path);
                resourceURI = new GeneralResourceURIImpl(site, url.getPath(), uuid);

                // Make sure the resource doesn't exist
                if (contentRepository.exists(new GeneralResourceURIImpl(site, url.getPath()))) {
                    logger.warn("Tried to create already existing resource {} in site '{}'", resourceURI, site);
                    throw new WebApplicationException(Status.CONFLICT);
                }
            } catch (IllegalArgumentException e) {
                logger.warn("Tried to create a resource with an invalid path '{}': {}", path, e.getMessage());
                throw new WebApplicationException(Status.BAD_REQUEST);
            } catch (ContentRepositoryException e) {
                logger.warn("Resource lookup {} failed for site '{}'", resourceURI, site);
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            }
        } else {
            resourceURI = new GeneralResourceURIImpl(site, "/" + uuid.replaceAll("-", ""), uuid);
        }

        URI uri = null;
        Resource<?> resource = null;
        try {
            // Parse the resource and store it
            logger.debug("Creating new resource at {}", resourceURI);
            resource = new FileResourceImpl(resourceURI);
            resource.setCreated(user, new Date());
            contentRepository.put(resource, true);
            uri = new URI(UrlUtils.concat(request.getRequestURL().toString(), resourceURI.getIdentifier()));
        } catch (URISyntaxException e) {
            logger.warn("Error creating a uri for resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (IOException e) {
            logger.warn("Error writing new resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (IllegalStateException e) {
            logger.warn("Illegal state while adding new resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.PRECONDITION_FAILED);
        } catch (ContentRepositoryException e) {
            logger.warn("Error adding new resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        // Create the response
        ResponseBuilder response = Response.created(uri);
        response.tag(ResourceUtils.getETagValue(resource));
        response.lastModified(ResourceUtils.getModificationDate(resource));
        return response.build();
    }

    /**
     * Removes the indicated resource from the site.
     * 
     * @param request
     *          the http request
     * @param resourceId
     *          the resource identifier
     * @return response an empty response
     */
    @DELETE
    @Path("/{resource}")
    public Response deleteFile(@Context HttpServletRequest request, @PathParam("resource") String resourceId) {

        // Check the parameters
        if (resourceId == null)
            return Response.status(Status.BAD_REQUEST).build();

        Site site = getSite(request);
        WritableContentRepository contentRepository = (WritableContentRepository) getContentRepository(site, true);

        // Get the current user
        User user = securityService.getUser();
        if (user == null)
            throw new WebApplicationException(Status.UNAUTHORIZED);

        // Make sure the user has editing rights
        if (!SecurityUtils.userHasRole(user, SystemRole.EDITOR))
            throw new WebApplicationException(Status.UNAUTHORIZED);

        ResourceURI resourceURI = null;

        // Make sure the resource exists
        try {
            resourceURI = contentRepository.getResourceURI(resourceId);
            if (resourceURI == null) {
                logger.warn("Tried to delete non existing resource {} in site '{}'", resourceURI, site);
                throw new WebApplicationException(Status.NOT_FOUND);
            }
        } catch (ContentRepositoryException e) {
            logger.warn("File lookup {} failed for site '{}'", resourceURI, site);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        // Delete the resource
        try {
            // TODO: Versions?
            resourceURI = contentRepository.get(resourceURI).getURI();
            contentRepository.delete(resourceURI);
        } catch (SecurityException e) {
            logger.warn("Tried to delete file {} of site '{}' without permission", resourceURI, site);
            throw new WebApplicationException(Status.FORBIDDEN);
        } catch (ReferentialIntegrityException e) {
            logger.warn("Tried to delete referenced file {} of site '{}'", resourceURI, site);
            throw new WebApplicationException(Status.PRECONDITION_FAILED);
        } catch (IOException e) {
            logger.warn("Error deleting resource {} from site '{}': {}",
                    new Object[] { resourceURI, site, e.getMessage() });
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        } catch (ContentRepositoryException e) {
            logger.warn("Error removing resource {}: {}", resourceURI, e.getMessage());
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        return Response.ok().build();
    }

    /**
     * Creates a file resource at the site's content repository by uploading
     * initial file content and returns the location to post updates to.
     * 
     * @param request
     *          the http request
     * @param resourceXml
     *          the new resource
     * @param path
     *          the path to store the resource at
     * @param mimeType
     *          the content mime type
     * @return response the resource location
     */
    @POST
    @Path("/uploads")
    @Produces(MediaType.MEDIA_TYPE_WILDCARD)
    public Response uploadFile(@Context HttpServletRequest request) {

        Site site = getSite(request);

        // Make sure the content repository is writable
        if (site.getContentRepository().isReadOnly()) {
            logger.warn("Attempt to write to read-only content repository {}", site);
            throw new WebApplicationException(Status.PRECONDITION_FAILED);
        }

        String fileName = null;
        Language language = null;
        String path = null;
        String mimeType = null;
        File uploadedFile = null;

        try {
            // Multipart form encoding?
            if (ServletFileUpload.isMultipartContent(request)) {
                try {
                    ServletFileUpload payload = new ServletFileUpload();
                    for (FileItemIterator iter = payload.getItemIterator(request); iter.hasNext();) {
                        FileItemStream item = iter.next();
                        String fieldName = item.getFieldName();
                        if (item.isFormField()) {
                            String fieldValue = Streams.asString(item.openStream());
                            if (StringUtils.isBlank(fieldValue))
                                continue;
                            if (OPT_PATH.equals(fieldName)) {
                                path = fieldValue;
                            } else if (OPT_LANGUAGE.equals(fieldName)) {
                                try {
                                    language = LanguageUtils.getLanguage(fieldValue);
                                } catch (UnknownLanguageException e) {
                                    throw new WebApplicationException(Status.BAD_REQUEST);
                                }
                            } else if (OPT_MIMETYPE.equals(fieldName)) {
                                mimeType = fieldValue;
                            }
                        } else {
                            // once the body gets read iter.hasNext must not be invoked
                            // or the stream can not be read
                            fileName = StringUtils.trim(item.getName());
                            mimeType = StringUtils.trim(item.getContentType());
                            uploadedFile = File.createTempFile("upload-", null);
                            FileOutputStream fos = new FileOutputStream(uploadedFile);
                            try {
                                IOUtils.copy(item.openStream(), fos);
                            } catch (IOException e) {
                                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                            } finally {
                                IOUtils.closeQuietly(fos);
                            }
                        }
                    }

                } catch (FileUploadException e) {
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                } catch (IOException e) {
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                }
            }

            // Octet binary stream
            else {
                try {
                    fileName = StringUtils.trimToNull(request.getHeader("X-File-Name"));
                    path = StringUtils.trimToNull(request.getParameter(OPT_PATH));
                    mimeType = StringUtils.trimToNull(request.getParameter(OPT_MIMETYPE));
                    language = LanguageUtils.getLanguage(request.getParameter(OPT_LANGUAGE));
                } catch (UnknownLanguageException e) {
                    throw new WebApplicationException(Status.BAD_REQUEST);
                }

                InputStream is = null;
                FileOutputStream fos = null;
                try {
                    is = request.getInputStream();
                    if (is == null)
                        throw new WebApplicationException(Status.BAD_REQUEST);
                    uploadedFile = File.createTempFile("upload-", null);
                    fos = new FileOutputStream(uploadedFile);
                    IOUtils.copy(is, fos);
                } catch (IOException e) {
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                } finally {
                    IOUtils.closeQuietly(is);
                    IOUtils.closeQuietly(fos);
                }

            }

            // Has there been a file in the request?
            if (uploadedFile == null)
                throw new WebApplicationException(Status.BAD_REQUEST);

            // Check the filename
            if (fileName == null) {
                logger.warn("No filename found for upload, request header 'X-File-Name' not specified");
                fileName = uploadedFile.getName();
            }

            // Make sure there is a language
            if (language == null) {
                language = LanguageUtils.getPreferredLanguage(request, site);
            }

            // A mime type would be nice as well
            if (StringUtils.isBlank(mimeType)) {
                mimeType = detectMimeTypeFromFile(fileName, uploadedFile);
                if (mimeType == null)
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            }

            // Set owner and date created
            User user = securityService.getUser();
            if (user == null)
                throw new WebApplicationException(Status.UNAUTHORIZED);

            // Make sure the user has editing rights
            if (!SecurityUtils.userHasRole(user, SystemRole.EDITOR))
                throw new WebApplicationException(Status.UNAUTHORIZED);

            WritableContentRepository contentRepository = (WritableContentRepository) getContentRepository(site,
                    true);

            // Create the resource uri
            URI uri = null;
            InputStream is = null;
            Resource<?> resource = null;
            ResourceURI resourceURI = null;
            logger.debug("Adding resource to {}", resourceURI);
            ResourceSerializer<?, ?> serializer = serializerService.getSerializerByMimeType(mimeType);
            if (serializer == null) {
                logger.debug("No specialized resource serializer found, using regular file serializer");
                serializer = serializerService.getSerializerByType(FileResource.TYPE);
            }

            // Create the resource
            try {
                is = new FileInputStream(uploadedFile);
                resource = serializer.newResource(site, is, user, language);
                resourceURI = resource.getURI();
            } catch (FileNotFoundException e) {
                logger.warn("Error creating resource at {} from image: {}", uri, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } finally {
                IOUtils.closeQuietly(is);
            }

            // If a path has been specified, set it
            if (path != null && StringUtils.isNotBlank(path)) {
                try {
                    if (!path.startsWith("/"))
                        path = "/" + path;
                    WebUrl url = new WebUrlImpl(site, path);
                    resourceURI.setPath(url.getPath());

                    // Make sure the resource doesn't exist
                    if (contentRepository.exists(new GeneralResourceURIImpl(site, url.getPath()))) {
                        logger.warn("Tried to create already existing resource {} in site '{}'", resourceURI, site);
                        throw new WebApplicationException(Status.CONFLICT);
                    }
                } catch (IllegalArgumentException e) {
                    logger.warn("Tried to create a resource with an invalid path '{}': {}", path, e.getMessage());
                    throw new WebApplicationException(Status.BAD_REQUEST);
                } catch (ContentRepositoryException e) {
                    logger.warn("Resource lookup {} failed for site '{}'", resourceURI, site);
                    throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
                }
            }

            // Store the new resource
            try {
                uri = new URI(resourceURI.getIdentifier());
                contentRepository.put(resource, true);
            } catch (URISyntaxException e) {
                logger.warn("Error creating a uri for resource {}: {}", resourceURI, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (IOException e) {
                logger.warn("Error writing new resource {}: {}", resourceURI, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (IllegalStateException e) {
                logger.warn("Illegal state while adding new resource {}: {}", resourceURI, e.getMessage());
                throw new WebApplicationException(Status.PRECONDITION_FAILED);
            } catch (ContentRepositoryException e) {
                logger.warn("Error adding new resource {}: {}", resourceURI, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            }

            ResourceContent content = null;
            ResourceContentReader<?> reader = null;
            try {
                reader = serializer.getContentReader();
                is = new FileInputStream(uploadedFile);
                content = reader.createFromContent(is, user, language, uploadedFile.length(), fileName, mimeType);
            } catch (IOException e) {
                logger.warn("Error reading resource content {} from request", uri);
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (ParserConfigurationException e) {
                logger.warn("Error configuring parser to read resource content {}: {}", uri, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (SAXException e) {
                logger.warn("Error parsing udpated resource {}: {}", uri, e.getMessage());
                throw new WebApplicationException(Status.BAD_REQUEST);
            } catch (Throwable t) {
                logger.warn("Unknown error while trying to read resource content {}: {}", uri, t.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } finally {
                IOUtils.closeQuietly(is);
                if (content == null) {
                    try {
                        contentRepository.delete(resourceURI);
                    } catch (Throwable t) {
                        logger.error("Error deleting orphan resource {}", resourceURI, t);
                    }
                }
            }

            try {
                is = new FileInputStream(uploadedFile);
                resource = contentRepository.putContent(resource.getURI(), content, is);
            } catch (IOException e) {
                logger.warn("Error writing content to resource {}: {}", uri, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } catch (IllegalStateException e) {
                logger.warn("Illegal state while adding content to resource {}: {}", uri, e.getMessage());
                throw new WebApplicationException(Status.PRECONDITION_FAILED);
            } catch (ContentRepositoryException e) {
                logger.warn("Error adding content to resource {}: {}", uri, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            } finally {
                IOUtils.closeQuietly(is);
            }

            // Create the response
            ResponseBuilder response = Response.created(uri);
            response.type(MediaType.MEDIA_TYPE_WILDCARD);
            response.tag(ResourceUtils.getETagValue(resource));
            response.lastModified(ResourceUtils.getModificationDate(resource));
            return response.build();

        } finally {
            FileUtils.deleteQuietly(uploadedFile);
        }
    }

    /**
     * Returns the endpoint documentation.
     * 
     * @return the endpoint documentation
     */
    @GET
    @Path("/docs")
    @Produces(MediaType.TEXT_HTML)
    public String getDocumentation(@Context HttpServletRequest request) {
        if (docs == null) {
            String docsPath = request.getRequestURI();
            String docsPathExtension = request.getPathInfo();
            String servicePath = request.getRequestURI().substring(0,
                    docsPath.length() - docsPathExtension.length());
            docs = FilesEndpointDocs.createDocumentation(servicePath);
        }
        return docs;
    }

    /**
     * Callback from OSGi to set the security service.
     * 
     * @param securityService
     *          the security service
     */
    void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    /**
     * Try to detect the mimetype from filename or file.
     * 
     * @param fileName
     *          the file name
     * @param uploadedFile
     *          the uploaded file
     * @return the mimetype or <code>null</code> if no mimetype could be detected
     */
    private String detectMimeTypeFromFile(String fileName, File uploadedFile) {
        String mimeType = null;
        if (fileName.endsWith(".ogg")) {
            mimeType = "video/ogg";
        } else if (fileName.endsWith(".mp4")) {
            mimeType = "video/mp4";
        } else if (fileName.endsWith(".webm")) {
            mimeType = "video/webm";
        } else {
            mimeType = mimeTypeDetector.detect(fileName);
        }
        if (!StringUtils.isBlank(mimeType))
            return mimeType;

        InputStream is = null;
        try {
            is = new FileInputStream(uploadedFile);
            mimeType = mimeTypeDetector.detect(is);
        } catch (IOException e) {
            logger.warn("Error detecting mime type: {}", e.getMessage());
        } finally {
            IOUtils.closeQuietly(is);
        }
        return mimeType;
    }

    /**
     * Loads the files from the site's content repository.
     * 
     * @param q
     *          the search query
     * @return the files
     * @throws WebApplicationException
     *           if the content repository is unavailable or if the content can't
     *           be loaded
     */
    private String loadResultSet(SearchQuery q) throws WebApplicationException {
        ContentRepository repository = getContentRepository(q.getSite(), false);
        if (repository == null)
            throw new WebApplicationException(Status.SERVICE_UNAVAILABLE);

        SearchResult result = null;
        try {
            result = repository.find(q);
        } catch (ContentRepositoryException e) {
            logger.warn(e.getMessage());
            throw new WebApplicationException();
        }

        StringBuffer buf = new StringBuffer("<files ");
        buf.append("hits=\"").append(result.getHitCount()).append("\" ");
        buf.append("offset=\"").append(result.getOffset()).append("\" ");
        if (q.getLimit() > 0)
            buf.append("limit=\"").append(result.getLimit()).append("\" ");
        buf.append("page=\"").append(result.getPage()).append("\" ");
        buf.append("pagesize=\"").append(result.getPageSize()).append("\"");
        buf.append(">");
        for (SearchResultItem item : result.getItems()) {
            String xml = ((ResourceSearchResultItem) item).getResourceXml();

            // TODO: Remove this hack once the importer is fixed
            xml = xml.replace("292278994-08-17T07:12:55Z", "2010-08-17T07:12:55Z");

            buf.append(xml);
        }
        buf.append("</files>");
        return buf.toString();
    }

    /**
     * OSGi callback that is setting the resource serializer.
     * 
     * @param serializer
     *          the resource serializer service
     */
    void setResourceSerializer(ResourceSerializerService serializer) {
        this.serializerService = serializer;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "File rest endpoint";
    }

}