org.opendatakit.api.odktables.FileManifestService.java Source code

Java tutorial

Introduction

Here is the source code for org.opendatakit.api.odktables.FileManifestService.java

Source

/*
 * Copyright (C) 2012-2013 University of Washington
 *
 * 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.opendatakit.api.odktables;

import java.net.MalformedURLException;
import java.net.URI;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opendatakit.aggregate.odktables.rest.ApiConstants;
import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesFileManifest;
import org.opendatakit.aggregate.odktables.rest.entity.OdkTablesFileManifestEntry;
import org.opendatakit.context.CallingContext;
import org.opendatakit.odktables.FileManifestManager;
import org.opendatakit.odktables.exception.PermissionDeniedException;
import org.opendatakit.odktables.relation.DbManifestETags;
import org.opendatakit.odktables.relation.DbManifestETags.DbManifestETagEntity;
import org.opendatakit.persistence.exception.ODKDatastoreException;
import org.opendatakit.persistence.exception.ODKEntityNotFoundException;
import org.opendatakit.persistence.exception.ODKOverQuotaException;
import org.opendatakit.persistence.exception.ODKTaskLockException;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.Authorization;

/**
 * Servlet for downloading a manifest of files to the phone for the correct app and the correct
 * table.
 *
 * @author sudar.sam@gmail.com
 */
@Api(authorizations = { @Authorization(value = "basicAuth") })
public class FileManifestService {

    private final CallingContext cc;
    private final String appId;
    private final UriInfo info;

    public FileManifestService(UriInfo info, String appId, CallingContext cc) throws ODKEntityNotFoundException,
            ODKDatastoreException, PermissionDeniedException, ODKTaskLockException {
        this.cc = cc;
        this.appId = appId;
        this.info = info;
    }

    public static String getAppLevelManifestETag(CallingContext cc) throws ODKDatastoreException {
        DbManifestETagEntity eTagEntity = DbManifestETags.getTableIdEntry(DbManifestETags.APP_LEVEL, cc);
        return eTagEntity.getManifestETag();
    }

    public static String getTableLevelManifestETag(String tableId, CallingContext cc) throws ODKDatastoreException {
        DbManifestETagEntity eTagEntity = DbManifestETags.getTableIdEntry(tableId, cc);
        return eTagEntity.getManifestETag();
    }

    /**
     *
     * @param httpHeaders
     * @param odkClientVersion
     * @return {@link OdkTablesFileManifest} of all the files meeting the filter criteria.
     * @throws ODKOverQuotaException
     * @throws ODKEntityNotFoundException
     * @throws ODKTaskLockException
     * @throws ODKDatastoreException
     * @throws PermissionDeniedException
     */
    @GET
    @ApiOperation(value = "Returns a list of application-level files.", response = OdkTablesFileManifest.class)
    @Path("{odkClientVersion}")
    @Produces({ MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8,
            ApiConstants.MEDIA_APPLICATION_XML_UTF8 })
    public Response /* OdkTablesFileManifest */ getAppLevelFileManifest(@Context HttpHeaders httpHeaders,
            @PathParam("odkClientVersion") String odkClientVersion) throws ODKEntityNotFoundException,
            ODKOverQuotaException, PermissionDeniedException, ODKDatastoreException, ODKTaskLockException {

        FileManifestManager manifestManager = new FileManifestManager(appId, odkClientVersion, cc);
        OdkTablesFileManifest manifest = null;
        Log log = LogFactory.getLog(FileManifestService.class);

        // retrieve the incoming if-none-match eTag...
        List<String> eTags = httpHeaders.getRequestHeader(HttpHeaders.IF_NONE_MATCH);
        String eTag = (eTags == null || eTags.isEmpty()) ? null : eTags.get(0);
        DbManifestETagEntity eTagEntity = null;
        try {
            try {
                eTagEntity = DbManifestETags.getTableIdEntry(DbManifestETags.APP_LEVEL, cc);
            } catch (ODKEntityNotFoundException e) {
                // ignore...
            }
            if (eTag != null && eTagEntity != null && eTag.equals(eTagEntity.getManifestETag())) {
                log.info("The etag about to be returned for Not Modified is " + eTag);
                return Response.status(Status.NOT_MODIFIED).header(HttpHeaders.ETAG, eTag)
                        .header(ApiConstants.OPEN_DATA_KIT_VERSION_HEADER, ApiConstants.OPEN_DATA_KIT_VERSION)
                        .header("Access-Control-Allow-Origin", "*")
                        .header("Access-Control-Allow-Credentials", "true").build();
            }
            // we want just the app-level files.
            manifest = manifestManager.getManifestForAppLevelFiles();

        } catch (ODKDatastoreException e) {
            log.error("Datastore exception in getting the file manifest");
            e.printStackTrace();
        }
        if (manifest == null) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Unable to retrieve manifest.")
                    .header(ApiConstants.OPEN_DATA_KIT_VERSION_HEADER, ApiConstants.OPEN_DATA_KIT_VERSION)
                    .header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Credentials", "true")
                    .build();
        } else {
            String newETag = Integer.toHexString(manifest.hashCode());
            // create a new eTagEntity if there isn't one already...
            if (eTagEntity == null) {
                eTagEntity = DbManifestETags.createNewEntity(DbManifestETags.APP_LEVEL, cc);
                eTagEntity.setManifestETag(newETag);
                eTagEntity.put(cc);
            } else if (!newETag.equals(eTagEntity.getManifestETag())) {
                log.error("App-level Manifest ETag does not match computed value!");
                eTagEntity.setManifestETag(newETag);
                eTagEntity.put(cc);
            }
            // and whatever the eTag is in that entity is the eTag we should return...
            eTag = eTagEntity.getManifestETag();
            UriBuilder uriBuilder = info.getBaseUriBuilder();
            uriBuilder.path(OdkTables.class);
            //UriBuilder uriBuilder = UriBuilder.fromResource(OdkTables.class);
            uriBuilder.path(OdkTables.class, "getFilesService");
            // now supply the downloadUrl...
            for (OdkTablesFileManifestEntry entry : manifest.getFiles()) {
                URI self = uriBuilder.clone().path(FileService.class, "getFile")
                        .build(ArrayUtils.toArray(appId, odkClientVersion, entry.filename), false);
                try {
                    entry.downloadUrl = self.toURL().toExternalForm();
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                    throw new IllegalArgumentException("Unable to convert to URL");
                }
            }

            log.info("The etag about to be returned for OK is " + eTag);
            return Response.ok(manifest).header(HttpHeaders.ETAG, eTag)
                    .header(ApiConstants.OPEN_DATA_KIT_VERSION_HEADER, ApiConstants.OPEN_DATA_KIT_VERSION)
                    .header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Credentials", "true")
                    .build();
        }
    }

    /**
     *
     * @param httpHeaders
     * @param odkClientVersion
     * @param tableId
     * @return {@link OdkTablesFileManifest} of all the files meeting the filter criteria.
     * @throws ODKOverQuotaException
     * @throws ODKEntityNotFoundException
     * @throws ODKTaskLockException
     * @throws ODKDatastoreException
     * @throws PermissionDeniedException
     */
    @GET
    @ApiOperation(value = "Returns a the list of files which make up a form definition.", response = OdkTablesFileManifest.class)
    @Path("{odkClientVersion}/{tableId}")
    @Produces({ MediaType.APPLICATION_JSON, ApiConstants.MEDIA_TEXT_XML_UTF8,
            ApiConstants.MEDIA_APPLICATION_XML_UTF8 })
    public Response /* OdkTablesFileManifest */ getTableIdFileManifest(@Context HttpHeaders httpHeaders,
            @PathParam("odkClientVersion") String odkClientVersion, @PathParam("tableId") String tableId)
            throws ODKEntityNotFoundException, ODKOverQuotaException, PermissionDeniedException,
            ODKDatastoreException, ODKTaskLockException {

        FileManifestManager manifestManager = new FileManifestManager(appId, odkClientVersion, cc);
        OdkTablesFileManifest manifest = null;

        // retrieve the incoming if-none-match eTag...
        List<String> eTags = httpHeaders.getRequestHeader(HttpHeaders.IF_NONE_MATCH);
        String eTag = (eTags == null || eTags.isEmpty()) ? null : eTags.get(0);
        DbManifestETagEntity eTagEntity = null;
        try {
            try {
                eTagEntity = DbManifestETags.getTableIdEntry(tableId, cc);
            } catch (ODKEntityNotFoundException e) {
                // ignore...
            }
            if (eTag != null && eTagEntity != null && eTag.equals(eTagEntity.getManifestETag())) {
                return Response.status(Status.NOT_MODIFIED).header(HttpHeaders.ETAG, eTag)
                        .header(ApiConstants.OPEN_DATA_KIT_VERSION_HEADER, ApiConstants.OPEN_DATA_KIT_VERSION)
                        .header("Access-Control-Allow-Origin", "*")
                        .header("Access-Control-Allow-Credentials", "true").build();
            }
            // we want just the files for the table.
            manifest = manifestManager.getManifestForTable(tableId);
        } catch (ODKDatastoreException e) {
            Log log = LogFactory.getLog(FileManifestService.class);
            log.error("Datastore exception in getting the file manifest");
            e.printStackTrace();
        }
        if (manifest == null) {
            return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Unable to retrieve manifest.")
                    .header(ApiConstants.OPEN_DATA_KIT_VERSION_HEADER, ApiConstants.OPEN_DATA_KIT_VERSION)
                    .header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Credentials", "true")
                    .build();
        } else {
            String newETag = Integer.toHexString(manifest.hashCode());
            // create a new eTagEntity if there isn't one already...
            if (eTagEntity == null) {
                eTagEntity = DbManifestETags.createNewEntity(tableId, cc);
                eTagEntity.setManifestETag(newETag);
                eTagEntity.put(cc);
            } else if (!newETag.equals(eTagEntity.getManifestETag())) {
                Log log = LogFactory.getLog(FileManifestService.class);
                log.error("Table-level (" + tableId + ") Manifest ETag does not match computed value!");
                eTagEntity.setManifestETag(newETag);
                eTagEntity.put(cc);
            }
            // and whatever the eTag is in that entity is the eTag we should return...
            eTag = eTagEntity.getManifestETag();
            fixDownloadUrls(info, appId, odkClientVersion, manifest);

            return Response.ok(manifest).header(HttpHeaders.ETAG, eTag)
                    .header(ApiConstants.OPEN_DATA_KIT_VERSION_HEADER, ApiConstants.OPEN_DATA_KIT_VERSION)
                    .header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Credentials", "true")
                    .build();
        }
    }

    public static void fixDownloadUrls(UriInfo info, String appId, String odkClientVersion,
            OdkTablesFileManifest manifest) {
        UriBuilder ub = info.getBaseUriBuilder();
        ub.path(OdkTables.class);
        ub.path(OdkTables.class, "getFilesService");
        // now supply the downloadUrl...
        for (OdkTablesFileManifestEntry entry : manifest.getFiles()) {
            URI self = ub.clone().path(FileService.class, "getFile")
                    .build(ArrayUtils.toArray(appId, odkClientVersion, entry.filename), false);
            try {
                entry.downloadUrl = self.toURL().toExternalForm();
            } catch (MalformedURLException e) {
                e.printStackTrace();
                throw new IllegalArgumentException("Unable to convert to URL");
            }
        }

    }

}