org.dishevelled.variation.googlegenomics.GoogleGenomicsFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.dishevelled.variation.googlegenomics.GoogleGenomicsFactory.java

Source

/*
    
dsh-variation  Variation.
Copyright (c) 2013-2015 held jointly by the individual authors.
    
This library 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 3 of the License, or (at
your option) any later version.
    
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; with out 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 library;  if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
    
> http://www.fsf.org/licensing/licenses/lgpl.html
> http://www.opensource.org/licenses/lgpl-license.php
    
*/
package org.dishevelled.variation.googlegenomics;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.File;
import java.io.InputStreamReader;
import java.io.IOException;

import java.security.GeneralSecurityException;

import java.util.List;
import java.util.Objects;

import java.util.concurrent.TimeUnit;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import com.google.common.collect.ImmutableList;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.auth.oauth2.TokenResponse;

import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;

import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;

import com.google.api.client.http.javanet.NetHttpTransport;

import com.google.api.client.json.JsonFactory;

import com.google.api.client.json.jackson2.JacksonFactory;

import com.google.api.client.util.store.FileDataStoreFactory;

import com.google.api.services.genomics.Genomics;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Factory methods for the Google Genomics API.
 *
 * @author  Michael Heuer
 */
public final class GoogleGenomicsFactory {
    /** Application name. */
    private static final String APPLICATION_NAME = "Variation/1.0-SNAPSHOT";

    /** Data store directory. */
    private static final File DATA_STORE_DIR = new File(System.getProperty("user.home"), ".variation/data-store");

    /** Client secrets resource name. */
    private static final String CLIENT_SECRETS = "client_secrets.json";

    /** Genomics scope. */
    private static final String GENOMICS_SCOPE = "https://www.googleapis.com/auth/genomics.readonly";

    /** List of scopes. */
    private static final List<String> SCOPES = ImmutableList.of(GENOMICS_SCOPE);

    /** Redirect URI. */
    private static final String REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob"; // also com.google.api.client.googleapis.auth.oauth2.GoogleOAuthConstants.OOB_REDIRECT_URI

    /** JSON factory. */
    private final JsonFactory jsonFactory;

    /** Client secrets. */
    private final GoogleClientSecrets clientSecrets;

    /** HTTP transport. */
    private final NetHttpTransport httpTransport;

    /** Data store factory. */
    private final FileDataStoreFactory dataStoreFactory;

    /** Cache of genomics APIs keyed by root URL, authorization code, and authorization code flow. */
    private final LoadingCache<GenomicsKey, Genomics> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(3540, TimeUnit.SECONDS).build(new CacheLoader<GenomicsKey, Genomics>() {
                @Override
                public Genomics load(final GenomicsKey genomicsKey) throws IOException {
                    return createGenomics(genomicsKey);
                }
            });

    /** Logger. */
    private final Logger logger = LoggerFactory.getLogger(GoogleGenomicsFactory.class);

    /**
     * Create a new Google Genomics API factory.
     */
    public GoogleGenomicsFactory() {
        jsonFactory = createJsonFactory();
        clientSecrets = createGoogleClientSecrets(jsonFactory, CLIENT_SECRETS);
        httpTransport = createNetHttpTransport();
        dataStoreFactory = createFileDataStoreFactory(DATA_STORE_DIR);

        if (logger.isInfoEnabled()) {
            logger.info("created google genomics factory with client id {}",
                    clientSecrets.getDetails().getClientId());
        }
    }

    /**
     * Start a new Google Genomics API authorization code flow.
     *
     * @return a new Google Genomics API authorization code flow
     * @throws IOException if an I/O error occurs
     */
    public GoogleAuthorizationCodeFlow startFlow() throws IOException {
        if (logger.isInfoEnabled()) {
            logger.info("starting new google genomics authorization code flow");
        }
        return new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, clientSecrets, SCOPES)
                .setDataStoreFactory(dataStoreFactory).build();
    }

    /**
     * Return the authorization URL for the specified Google Genomics API authorization code flow.
     *
     * @param googleAuthorizationCodeFlow Google Genomics API authorization code flow, must not be null
     * @return the authorization URL for the specified Google Genomics API authorization code flow
     */
    public String authorizationUrl(final GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow) {
        checkNotNull(googleAuthorizationCodeFlow);
        String authorizationUrl = googleAuthorizationCodeFlow.newAuthorizationUrl().setRedirectUri(REDIRECT_URI)
                .build();
        if (logger.isInfoEnabled()) {
            logger.info("created new google genomics authorization url {}", authorizationUrl);
        }
        return authorizationUrl;
    }

    /**
     * Create and return a new Google Genomics API for the specified root URL, authorization code, and authorization code flow.
     *
     * @param rootUrl root URL, must not be null
     * @param authorizationCode authorization code, must not be null
     * @param googleAuthorizationCodeFlow Google Genomics API authorization code flow, must not be null
     * @throws IOException if an I/O error occurs
     */
    public Genomics genomics(final String rootUrl, final String authorizationCode,
            final GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow) throws IOException {
        checkNotNull(rootUrl, "root url must not be null");
        checkArgument(rootUrl.length() > 0, "root url must not be empty");
        checkNotNull(authorizationCode, "authorization code must not be null");
        checkArgument(authorizationCode.length() > 0, "authorization code must not be empty");
        checkNotNull(googleAuthorizationCodeFlow, "authorizaton code flow must not be null");
        return cache.getUnchecked(new GenomicsKey(rootUrl, authorizationCode, googleAuthorizationCodeFlow));
    }

    Genomics createGenomics(final GenomicsKey genomicsKey) throws IOException {
        final String rootUrl = genomicsKey.rootUrl();
        final String authorizationCode = genomicsKey.authorizationCode();
        final GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow = genomicsKey.googleAuthorizationCodeFlow();
        if (logger.isInfoEnabled()) {
            logger.info("creating new google genomics api for root url {} authorization code {}", rootUrl,
                    abbrev(authorizationCode));
        }
        TokenResponse tokenResponse = googleAuthorizationCodeFlow.newTokenRequest(authorizationCode)
                .setRedirectUri(REDIRECT_URI).execute();
        if (logger.isInfoEnabled()) {
            logger.info("received token response {}", abbrev(tokenResponse.getAccessToken()));
        }
        final Credential credential = googleAuthorizationCodeFlow.createAndStoreCredential(tokenResponse, "user");
        if (logger.isInfoEnabled()) {
            logger.info("received credential {} expires in {} s", abbrev(credential.getAccessToken()),
                    credential.getExpiresInSeconds());
        }
        Genomics genomics = new Genomics.Builder(httpTransport, jsonFactory, credential)
                .setApplicationName(APPLICATION_NAME).setRootUrl(rootUrl).setServicePath("/")
                .setHttpRequestInitializer(new HttpRequestInitializer() {
                    @Override
                    public void initialize(final HttpRequest httpRequest) throws IOException {
                        credential.initialize(httpRequest);
                        httpRequest.setReadTimeout(60000); // 60 seconds                                                                                            
                    }
                }).build();

        if (logger.isInfoEnabled()) {
            logger.info("created new google genomics api for root URL {} authorization code {} application name {}",
                    rootUrl, abbrev(authorizationCode),
                    genomics.getApplicationName() == null ? "null" : genomics.getApplicationName());
        }
        return genomics;
    }

    // could be guice providers I suppose . . .

    JsonFactory createJsonFactory() {
        return JacksonFactory.getDefaultInstance();
    }

    GoogleClientSecrets createGoogleClientSecrets(final JsonFactory jsonFactory, final String resourceName) {
        try {
            return GoogleClientSecrets.load(jsonFactory,
                    new InputStreamReader(getClass().getResourceAsStream(resourceName)));
        } catch (IOException e) {
            logger.error("unable to load client secrets", e);
            throw new RuntimeException("unable to load client secrets", e);
        }
    }

    NetHttpTransport createNetHttpTransport() {
        try {
            return GoogleNetHttpTransport.newTrustedTransport();
        } catch (GeneralSecurityException e) {
            logger.error("unable to create HTTP transport", e);
            throw new RuntimeException("unable to create HTTP transport", e);
        } catch (IOException e) {
            logger.error("unable to create HTTP transport", e);
            throw new RuntimeException("unable to create HTTP transport", e);
        }
    }

    FileDataStoreFactory createFileDataStoreFactory(final File dataStore) {
        try {
            return new FileDataStoreFactory(dataStore);
        } catch (IOException e) {
            logger.error("unable to create data store factory", e);
            throw new RuntimeException("unable to create data store factory", e);
        }
    }

    private static String abbrev(final String value) {
        return value == null ? "null" : value.substring(0, Math.min(value.length(), 8)) + "...";
    }

    /**
     * Compound key of root URL, authorization code, and authorization code flow.
     */
    private static class GenomicsKey {
        private final String rootUrl;
        private final String authorizationCode;
        private final GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow;
        private final int hashCode;

        private GenomicsKey(final String rootUrl, final String authorizationCode,
                final GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow) {
            this.rootUrl = rootUrl;
            this.authorizationCode = authorizationCode;
            this.googleAuthorizationCodeFlow = googleAuthorizationCodeFlow;
            hashCode = Objects.hash(rootUrl, authorizationCode, googleAuthorizationCodeFlow);
        }

        private String rootUrl() {
            return rootUrl;
        }

        private String authorizationCode() {
            return authorizationCode;
        }

        private GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow() {
            return googleAuthorizationCodeFlow;
        }

        @Override
        public boolean equals(final Object o) {
            if (o == this) {
                return true;
            }
            if (!(o instanceof GenomicsKey)) {
                return false;
            }
            GenomicsKey genomicsKey = (GenomicsKey) o;
            return Objects.equals(rootUrl, genomicsKey.rootUrl)
                    && Objects.equals(authorizationCode, genomicsKey.authorizationCode)
                    && Objects.equals(googleAuthorizationCodeFlow, googleAuthorizationCodeFlow);
        }

        @Override
        public int hashCode() {
            return hashCode;
        }
    }
}