com.illumina.basespace.DefaultApiClient.java Source code

Java tutorial

Introduction

Here is the source code for com.illumina.basespace.DefaultApiClient.java

Source

/**
 * Copyright 2013 Illumina
 * 
 * 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 com.illumina.basespace;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.illumina.basespace.auth.ResourceForbiddenException;
import com.illumina.basespace.entity.ApiResource;
import com.illumina.basespace.entity.AppResultCompact;
import com.illumina.basespace.entity.AppSessionCompact;
import com.illumina.basespace.entity.AppSessionStatus;
import com.illumina.basespace.entity.CreateAppResult;
import com.illumina.basespace.entity.CreatePurchase;
import com.illumina.basespace.entity.File;
import com.illumina.basespace.entity.FileCompact;
import com.illumina.basespace.entity.FileRedirectMetaData;
import com.illumina.basespace.entity.ProductCompact;
import com.illumina.basespace.entity.ProjectCompact;
import com.illumina.basespace.entity.ReferenceCompact;
import com.illumina.basespace.entity.RunCompact;
import com.illumina.basespace.entity.SampleCompact;
import com.illumina.basespace.entity.UploadStatus;
import com.illumina.basespace.file.DownloadEvent;
import com.illumina.basespace.file.DownloadListener;
import com.illumina.basespace.infrastructure.BaseSpaceException;
import com.illumina.basespace.infrastructure.ClientConnectionProvider;
import com.illumina.basespace.infrastructure.ConversionContext;
import com.illumina.basespace.infrastructure.ResourceNotFoundException;
import com.illumina.basespace.param.CoverageParams;
import com.illumina.basespace.param.FileParams;
import com.illumina.basespace.param.Mappable;
import com.illumina.basespace.param.PositionalQueryParams;
import com.illumina.basespace.param.QueryParams;
import com.illumina.basespace.property.CreatePropertiesHolder;
import com.illumina.basespace.property.Property;
import com.illumina.basespace.response.CreatePurchaseResponse;
import com.illumina.basespace.response.GetAppResultResponse;
import com.illumina.basespace.response.GetAppSessionResponse;
import com.illumina.basespace.response.GetCoverageMetadataResponse;
import com.illumina.basespace.response.GetCoverageResponse;
import com.illumina.basespace.response.GetFileRedirectMetaDataResponse;
import com.illumina.basespace.response.GetFileResponse;
import com.illumina.basespace.response.GetFileUploadResponse;
import com.illumina.basespace.response.GetGenomeResponse;
import com.illumina.basespace.response.GetProjectResponse;
import com.illumina.basespace.response.GetPropertyResponse;
import com.illumina.basespace.response.GetPurchaseResponse;
import com.illumina.basespace.response.GetRefundResponse;
import com.illumina.basespace.response.GetRunResponse;
import com.illumina.basespace.response.GetSampleResponse;
import com.illumina.basespace.response.GetUserResponse;
import com.illumina.basespace.response.GetVariantSetResponse;
import com.illumina.basespace.response.ListAppResultsResponse;
import com.illumina.basespace.response.ListAppSessionsResponse;
import com.illumina.basespace.response.ListFilesResponse;
import com.illumina.basespace.response.ListGenomesResponse;
import com.illumina.basespace.response.ListProductsResponse;
import com.illumina.basespace.response.ListProjectsResponse;
import com.illumina.basespace.response.ListPropertiesResponse;
import com.illumina.basespace.response.ListRunsResponse;
import com.illumina.basespace.response.ListSamplesResponse;
import com.illumina.basespace.response.ListVariantsResponse;
import com.illumina.basespace.util.TypeHelper;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.representation.Form;
import com.sun.jersey.core.util.MultivaluedMapImpl;

/**
 * Default implementation of a BaseSpace client
 * 
 * @author bking
 */
class DefaultApiClient implements ApiClient {
    private final Logger logger = Logger.getLogger(DefaultApiClient.class.getPackage().getName());
    private ClientConnectionProvider connectionProvider;
    private Map<String, FileRedirectMetaData> downloadMetaDataCache = new Hashtable<String, FileRedirectMetaData>();

    /**
     * Create a BaseSpace session
     * 
     * @param config
     *            the configuration to use to establish the session
     */
    DefaultApiClient(final ClientConnectionProvider connectionProvider) {
        this.connectionProvider = connectionProvider;
        logger.setLevel(Level.ALL);
    }

    @Override
    public GetUserResponse getCurrentUser() {
        return getConnectionProvider().getResponse(GetUserResponse.class, "users/current", null, null);
    }

    @Override
    public ListProjectsResponse getProjects(QueryParams params) {
        return getConnectionProvider().getResponse(ListProjectsResponse.class, "users/current/projects", params,
                null);
    }

    @Override
    public GetProjectResponse getProject(String id) {
        return getConnectionProvider().getResponse(GetProjectResponse.class, "projects/" + id, null, null);
    }

    @Override
    public GetProjectResponse createProject(String projectName) {
        Form form = new Form();
        form.add("name", projectName);
        return getConnectionProvider().postForm(GetProjectResponse.class, "projects", null, form);
    }

    @Override
    public ListSamplesResponse getSamples(ProjectCompact project, QueryParams params) {
        return getConnectionProvider().getResponse(ListSamplesResponse.class,
                "projects/" + project.getId() + "/samples", params, null);

    }

    @Override
    public ListSamplesResponse getSamples(RunCompact run, QueryParams params) {
        return getConnectionProvider().getResponse(ListSamplesResponse.class, "runs/" + run.getId() + "/samples",
                params, null);

    }

    @Override
    public GetSampleResponse getSample(String id) {
        return getConnectionProvider().getResponse(GetSampleResponse.class, "samples/" + id, null, null);
    }

    @Override
    public GetAppResultResponse getAppResult(String id) {
        return getConnectionProvider().getResponse(GetAppResultResponse.class, "appresults/" + id, null, null);
    }

    @Override
    public ListAppResultsResponse getAppResults(ProjectCompact project, QueryParams params) {
        return getConnectionProvider().getResponse(ListAppResultsResponse.class,
                "projects/" + project.getId() + "/appresults", params, null);
    }

    /**
     * Updated API call to use appsessionS/... instead of appsession/
     */
    @Override
    public ListAppResultsResponse getAppResults(AppSessionCompact appSession, QueryParams params) {
        return getConnectionProvider().getResponse(ListAppResultsResponse.class,
                "appsessions/" + appSession.getId() + "/appresults", params, null);
    }

    @Override
    public GetAppSessionResponse getAppSession(String id) {
        return getConnectionProvider().getResponse(GetAppSessionResponse.class, "appsessions/" + id, null, null);
    }

    @Override
    public ListAppSessionsResponse getAppSessions(String appId, String status, QueryParams params) {
        if (params == null)
            params = new QueryParams();
        params.addParam("AppId", appId);
        params.addParam("Status", status);
        return getConnectionProvider().getResponse(ListAppSessionsResponse.class, "users/current/appsessions",
                params, null);

    }

    @Override
    public ListRunsResponse getRuns(QueryParams params) {
        return getConnectionProvider().getResponse(ListRunsResponse.class, "users/current/runs", params, null);
    }

    @Override
    public GetRunResponse getRun(String id) {
        return getConnectionProvider().getResponse(GetRunResponse.class, "runs/" + id, null, null);
    }

    @Override
    public ListFilesResponse getFiles(SampleCompact sample, FileParams params) {
        return getConnectionProvider().getResponse(ListFilesResponse.class, "samples/" + sample.getId() + "/files",
                params, null);

    }

    @Override
    public ListFilesResponse getFiles(RunCompact run, FileParams params) {
        return getConnectionProvider().getResponse(ListFilesResponse.class, "runs/" + run.getId() + "/files",
                params, null);
    }

    @Override
    public ListFilesResponse getFiles(AppResultCompact appresult, FileParams params) {
        return getConnectionProvider().getResponse(ListFilesResponse.class,
                "appresults/" + appresult.getId() + "/files", params, null);
    }

    @Override
    public GetFileResponse getFile(String id) {
        return getConnectionProvider().getResponse(GetFileResponse.class, "files/" + id, null, null);
    }

    @Override
    public URI getDownloadURI(FileCompact file) {
        WebResource resource = connectionProvider.getClient()
                .resource(UriBuilder.fromUri(connectionProvider.getConfiguration().getApiRootUri())
                        .path(connectionProvider.getConfiguration().getVersion()).build())
                .path("files").path(file.getId()).path("content");
        return resource.getURI();
    }

    @Override
    public InputStream getFileInputStream(FileCompact file) {
        return getFileInputStream(file, 0, file.getSize());
    }

    @Override
    public InputStream getFileInputStream(FileCompact file, long start, long end) {
        try {
            return getInputStreamInternal(file, start, end);
        } catch (RuntimeException t) {
            throw t;
        }
    }

    @Override
    public void download(final com.illumina.basespace.entity.File file, java.io.File target,
            DownloadListener listener) {
        FileOutputStream fos = null;
        InputStream in = null;
        boolean canceled = false;
        long progress = 0;
        try {
            if (target.isDirectory()) {
                if (!target.exists() && !target.mkdirs()) {
                    throw new IllegalArgumentException("Unable to create local folder " + target.toString());
                }
                target = new java.io.File(target, file.getName());
            }

            final int CHUNK_SIZE = 4096;
            in = getFileInputStream(file);
            fos = new FileOutputStream(target);

            byte[] outputByte = new byte[CHUNK_SIZE];
            int bytesRead = 0;
            while (!canceled && (bytesRead = in.read(outputByte, 0, CHUNK_SIZE)) != -1) {
                fos.write(outputByte, 0, bytesRead);
                progress += bytesRead;
                if (listener != null) {
                    DownloadEvent evt = new DownloadEvent(file, progress, file.getSize());
                    listener.progress(evt);
                    canceled = evt.isCanceled();
                }
            }
            fos.close();
            in.close();
            fos = null;
            in = null;
            if (listener != null)
                listener.complete(new DownloadEvent(file, progress, file.getSize()));
        } catch (BaseSpaceException bs) {
            throw bs;
        } catch (Throwable t) {
            throw new RuntimeException("Error during file download", t);
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (Throwable t) {
            }
            try {
                if (fos != null)
                    fos.close();
            } catch (Throwable t) {
            }
            if (canceled) {
                if (target != null) {
                    target.delete();
                    if (listener != null)
                        listener.canceled(new DownloadEvent(file, progress, file.getSize()));
                }
            }
        }
    }

    @Override
    public GetFileRedirectMetaDataResponse getFileRedirectMetaData(FileCompact file) {
        final MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
        queryParams.add("redirect", "meta");
        Mappable mappable = new Mappable() {
            @Override
            public MultivaluedMap<String, String> toMap() {
                return queryParams;
            }
        };
        return getConnectionProvider().getResponse(GetFileRedirectMetaDataResponse.class,
                getDownloadURI(file).toString(), mappable, null);
    }

    @Override
    public GetCoverageResponse getCoverage(File file, String chromosome, CoverageParams params) {
        return getConnectionProvider().getResponse(GetCoverageResponse.class,
                file.getHrefCoverage() + "/" + chromosome, params, null);
    }

    @Override
    public GetCoverageMetadataResponse getCoverageMetaData(File file, String chromosome) {
        return getConnectionProvider().getResponse(GetCoverageMetadataResponse.class,
                file.getHrefCoverage() + "/" + chromosome + "/meta", null, null);
    }

    @Override
    public GetVariantSetResponse getVariantSet(File file) {
        return getConnectionProvider().getResponse(GetVariantSetResponse.class, "/variantset/" + file.getId(), null,
                null);
    }

    @Override
    public ListVariantsResponse getVariants(File file, String chromosome, PositionalQueryParams params) {
        params.addParam("Format", "json");
        return getConnectionProvider().getResponse(ListVariantsResponse.class,
                file.getHrefVariants() + "/variants/" + chromosome, params, null);
    }

    @Override
    public String getVariantRawRecord(FileCompact file, String chromosome, PositionalQueryParams params) {
        params.addParam("Format", "vcf");
        return getConnectionProvider().getStringResponse("variantset/" + file.getId() + "/variants/" + chromosome,
                params, null);
    }

    private String accessToken;

    protected String getAccessToken() {
        return accessToken;
    }

    protected WebResource getRootApiWebResource() {
        return getClient().resource(UriBuilder.fromUri(connectionProvider.getConfiguration().getApiRootUri())
                .path(connectionProvider.getConfiguration().getVersion()).build());
    }

    private Client getClient() {
        return connectionProvider.getClient();
    }

    public ClientConnectionProvider getConnectionProvider() {
        return this.connectionProvider;
    }

    @Override
    public GetAppSessionResponse updateAppSession(AppSessionCompact appSession, AppSessionStatus status,
            String summary) {
        Form form = new Form();
        form.add("status", status.toString());
        if (summary != null)
            form.add("statussummary", summary);
        return getConnectionProvider().postForm(GetAppSessionResponse.class, "appsessions/" + appSession.getId(),
                null, form);
    }

    @Override
    public GetAppResultResponse createAppResult(ProjectCompact project, String name, String description,
            URI hrefAppSession, ReferenceCompact[] references) {
        CreateAppResult appResult = new CreateAppResult();
        appResult.setName(name);
        appResult.setDescription(description);
        appResult.setHrefAppSession(hrefAppSession);
        appResult.setReferences(references);
        return getConnectionProvider().postJson(GetAppResultResponse.class,
                "projects/" + project.getId() + "/appresults", null, appResult);
    }

    @Override
    public ListGenomesResponse getGenomes(QueryParams params) {
        return getConnectionProvider().getResponse(ListGenomesResponse.class, "genomes", params, null);
    }

    @Override
    public GetGenomeResponse getGenome(String id) {
        return getConnectionProvider().getResponse(GetGenomeResponse.class, "genomes/" + id, null, null);

    }

    @Override
    public GetFileResponse startMultipartUpload(AppResultCompact appResult, String fileName, String contentType,
            String directory) {
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Content-Type", contentType);
        Form form = new Form();
        form.add("name", fileName);
        form.add("multipart", String.valueOf(true));
        if (directory != null)
            form.add("directory", directory);
        return getConnectionProvider().postForm(GetFileResponse.class, "appresults/" + appResult.getId() + "/files",
                headers, form);
    }

    @Override
    public ListProductsResponse getProducts(String[] tags, String[] productIds, QueryParams params) {
        if (params == null)
            params = new QueryParams();
        params.addParam("Tags", tags, ",");
        params.addParam("ProductId", productIds, ",");
        return getConnectionProvider().getResponse(ListProductsResponse.class,
                connectionProvider.getConfiguration().getStoreRootUri(), "users/current/products", params, null);
    }

    @Override
    public CreatePurchaseResponse createPurchase(AppSessionCompact appSession, ProductCompact[] products) {
        CreatePurchase purchase = new CreatePurchase();
        purchase.setProducts(products);
        purchase.setAppSessionId(appSession.getId());
        return getConnectionProvider().postJson(CreatePurchaseResponse.class,
                connectionProvider.getConfiguration().getStoreRootUri(), "purchases", null, purchase);
    }

    @Override
    public GetPurchaseResponse getPurchase(String id) throws ResourceNotFoundException, ResourceForbiddenException {
        return getConnectionProvider().getResponse(GetPurchaseResponse.class,
                connectionProvider.getConfiguration().getStoreRootUri(), "purchases/" + id, null, null);

    }

    @Override
    public GetRefundResponse createRefund(String purchaseId, String refundSecret, String comment) {
        Form form = new Form();
        form.add("RefundSecret", refundSecret);
        form.add("Comment", comment);
        return getConnectionProvider().postForm(GetRefundResponse.class,
                connectionProvider.getConfiguration().getStoreRootUri(), "purchases/" + purchaseId + "/refund",
                null, form);
    }

    @Override
    public GetFileUploadResponse uploadFilePart(FileCompact file, int partNumber, String MD5Hash,
            InputStream part) {
        Map<String, String> headers = null;
        if (MD5Hash != null) {
            headers = new HashMap<String, String>();
            headers.put("Content-MD5", MD5Hash);
        }
        return getConnectionProvider().putFile(GetFileUploadResponse.class,
                "files/" + file.getId() + "/parts/" + String.valueOf(partNumber), headers, part);
    }

    @Override
    public GetFileUploadResponse uploadFilePart(FileCompact file, int partNumber, String MD5Hash, byte[] part) {
        Map<String, String> headers = null;
        if (MD5Hash != null) {
            headers = new HashMap<String, String>();
            headers.put("Content-MD5", MD5Hash);
        }
        return getConnectionProvider().putFile(GetFileUploadResponse.class,
                "files/" + file.getId() + "/parts/" + String.valueOf(partNumber), headers, part);
    }

    @Override
    public GetFileResponse setMultipartUploadStatus(FileCompact file, UploadStatus status) {
        Form form = new Form();
        form.add("uploadstatus", status.toString().toLowerCase());
        return getConnectionProvider().postForm(GetFileResponse.class, "files/" + file.getId(), null, form);
    }

    protected InputStream getInputStreamInternal(FileCompact file, long start, long end) {
        try {
            FileRedirectMetaData metaData = getRedirectMetaData(file);
            InputStream in = connectionProvider.getClient().resource(metaData.getHrefContent())
                    .accept(MediaType.APPLICATION_OCTET_STREAM).accept(MediaType.TEXT_HTML)
                    .accept(MediaType.APPLICATION_XHTML_XML).header("Range", "bytes=" + start + "-" + end)
                    .get(InputStream.class);
            return in;
        } catch (ResourceForbiddenException forbidden) {
            downloadMetaDataCache.remove(file.getId());
            throw forbidden;
        } catch (BaseSpaceException bs) {
            throw bs;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    protected FileRedirectMetaData getRedirectMetaData(FileCompact file) {
        FileRedirectMetaData metaData = downloadMetaDataCache.get(file.getId());
        if (metaData == null || new Date().after(metaData.getExpires())) {
            logger.warning("Redirect metaData for file id#" + file.getId() + "(" + file.getName()
                    + ") does not exist or expired...fetching...");
            metaData = getFileRedirectMetaData(file).get();
            downloadMetaDataCache.put(file.getId(), metaData);
        }
        return metaData;
    }

    protected Mappable merge(Mappable mappable, Map<String, String> more) {
        final MultivaluedMap<String, String> map = new MultivaluedMapImpl();
        if (mappable != null)
            map.putAll(mappable.toMap());
        if (more != null) {
            for (String key : more.keySet()) {
                map.putSingle(key, more.get(key));
            }
        }
        Mappable newMappable = new Mappable() {
            @Override
            public MultivaluedMap<String, String> toMap() {
                return map;
            }
        };
        return newMappable;
    }

    @Override
    public ListPropertiesResponse putProperties(ApiResource resource, Property[] properties) {
        CreatePropertiesHolder props = new CreatePropertiesHolder(properties);
        return getConnectionProvider().postJson(ListPropertiesResponse.class,
                TypeHelper.INSTANCE.getResourcePath(resource.getClass(), true) + "/" + resource.getId()
                        + "/properties",
                null, props);

    }

    @Override
    public ListPropertiesResponse getProperties(ApiResource resource) {
        return getConnectionProvider().getResponse(ListPropertiesResponse.class,
                TypeHelper.INSTANCE.getResourcePath(resource.getClass(), true) + "/" + resource.getId()
                        + "/properties",
                null, null);
    }

    @Override
    public GetPropertyResponse getProperty(ApiResource resource, String propertyName) {
        return getConnectionProvider().getResponse(GetPropertyResponse.class,
                TypeHelper.INSTANCE.getResourcePath(resource.getClass(), true) + "/" + resource.getId()
                        + "/properties/" + propertyName + "/items",
                null, null);
    }

    @Override
    public void deleteProperty(ApiResource resource, String propertyName) {
        getConnectionProvider().delete(TypeHelper.INSTANCE.getResourcePath(resource.getClass(), true) + "/"
                + resource.getId() + "/properties/" + propertyName, null);
    }

    /**
     * GET: users/current/appsessions?OutputProjects={1}
     */
    @Override
    public ListAppSessionsResponse getAppSessions(String projectId, QueryParams params) {
        if (params == null)
            params = new QueryParams();
        params.addParam("OutputProjects", projectId);
        return getConnectionProvider().getResponse(ListAppSessionsResponse.class, "users/current/appsessions",
                params, null);

    }

}