com.mxgraph.online.drive.FileServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.mxgraph.online.drive.FileServlet.java

Source

/*
 * Copyright (c) 2012 Google Inc.
 * Copyright (c) 2012 JGraph Ltd.
 *
 * 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.mxgraph.online.drive;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.json.JSONObject;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.model.File;
import com.google.api.services.oauth2.model.Userinfo;
import com.google.gson.Gson;
import com.mxgraph.online.drive.CredentialMediator.CodeExchangeException;
import com.mxgraph.online.drive.CredentialMediator.NoRefreshTokenException;
import com.mxgraph.online.drive.CredentialMediator.NoUserIdException;
import com.mxgraph.online.drive.model.ClientFile;

/**
 * Servlet providing a small API for the DrEdit JavaScript client to use in
 * manipulating files.  Each operation (GET, POST, PUT) issues requests to the
 * Google Drive API.
 *
 * @author vicfryzel@google.com (Vic Fryzel)
 */
@SuppressWarnings("serial")
public class FileServlet extends HttpServlet {
    protected static final HttpTransport TRANSPORT = new NetHttpTransport();

    protected static final JsonFactory JSON_FACTORY = new JacksonFactory();

    /**
     * Default MIME type of files created or handled by DrEdit.
     *
     * This is also set in the Google APIs Console under the Drive SDK tab.
     */
    public static final String DEFAULT_MIMETYPE = "application/mxe";

    /**
     * MIME type to use when sending responses back to DrEdit JavaScript client.
     */
    public static final String JSON_MIMETYPE = "application/json;charset=utf-8";

    /**
     * Path component under war/ to locate client_secrets.json file.
     */
    public static final String CLIENT_SECRETS_FILE_PATH = "/WEB-INF/client_secrets.json";

    /**
     * Loaded data from war/WEB-INF/client_secrets.json.
     */
    protected GoogleClientSecrets secrets;

    /**
     * Scopes for which to request access from the user.
     */
    public static final List<String> SCOPES = Arrays.asList(
            // Required to access and manipulate files.
            "https://www.googleapis.com/auth/drive.file",
            // Required to identify the user in our data store.
            "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile",
            "https://www.googleapis.com/auth/drive.install");

    protected final static String APP_INSTALL_URL = "https://chrome.google.com/webstore/detail/bghopifpoccdfeinlciloipnlpnkmnmo";

    /**
     * Given a {@code file_id} URI parameter, return a JSON representation
     * of the given file.
     */
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        Drive service = null;
        if (req.getParameter("status") != null) {
            boolean isValid = false;
            CredentialMediator credentialMediator = null;

            try {
                credentialMediator = getCredentialMediator(req, resp);
                boolean isEndSession = shouldInvalidateSession(req, credentialMediator);
                if (isEndSession) {
                    endSession(req, resp, credentialMediator);
                }

                String userId = (String) req.getSession().getAttribute(CredentialMediator.USER_ID_KEY);

                if (userId != null && credentialMediator != null
                        && credentialMediator.getStoredCredential(userId) != null) {
                    isValid = true;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

            if (isValid) {
                resp.setStatus(200);
                HttpSession session = req.getSession();
                Userinfo ui = new Userinfo();
                ui.setEmail((String) session.getAttribute(CredentialMediator.EMAIL_KEY));
                ui.setId((String) session.getAttribute(CredentialMediator.USER_ID_KEY));
                JSONObject json = new JSONObject(ui);
                resp.getWriter().write(json.toString());
            } else {
                resp.setStatus(500);
                try {
                    resp.getWriter().write(getAuthorizationUrl(req, false));
                } catch (Exception ex) {
                    throw new RuntimeException("Failed to redirect for authorization.");
                }
            }
        } else {
            try {
                CredentialMediator credentialMediator = getCredentialMediator(req, resp);
                boolean isEndSession = shouldInvalidateSession(req, credentialMediator);
                if (isEndSession) {
                    endSession(req, resp, credentialMediator);
                }

                service = getDriveService(req, resp, credentialMediator);

                String fileId = req.getParameter("file_id");

                if (fileId == null) {
                    sendError(resp, 400, "The `file_id` URI parameter must be specified.");
                    return;
                }

                File file = null;
                try {
                    file = service.files().get(fileId).execute();
                } catch (GoogleJsonResponseException e) {
                    if (e.getStatusCode() == 401) {
                        // The user has revoked our token or it is otherwise bad.
                        // Delete the local copy so that their next page load will recover.
                        deleteCredential(req, resp);
                        sendError(resp, 401, "Unauthorized");
                        return;
                    } else if (e.getStatusCode() == 403) {
                        resp.getWriter().write(getAuthorizationUrl(req, false));
                        return;
                    }
                }

                if (file != null) {
                    String content = downloadFileContent(service, file);
                    if (content == null) {
                        content = "";
                    }
                    resp.setContentType(JSON_MIMETYPE);
                    String userId = (String) req.getSession().getAttribute(CredentialMediator.USER_ID_KEY);
                    String email = (String) req.getSession().getAttribute(CredentialMediator.EMAIL_KEY);
                    ClientFile cf = new ClientFile(file, content);
                    JSONObject json = new JSONObject();
                    json.put("resource_id", cf.resource_id);
                    json.put("title", cf.title);
                    json.put("description", cf.description);
                    json.put("mimeType", cf.mimeType);
                    json.put("content", cf.content);
                    json.put("email", email);
                    json.put("id", userId);

                    resp.getWriter().print(json.toString());
                } else {
                    sendError(resp, 404, "File not found");
                }
            } catch (Exception e) {
                //e.printStackTrace();
                resp.setStatus(500);
                try {
                    resp.getWriter().write(getAuthorizationUrl(req, false));
                } catch (Exception ex) {
                    throw new RuntimeException("Failed to redirect for authorization.");
                }
            }
        }
    }

    /**
     * Retrieve the authorization URL to authorize the user with the given
     * email address.
     *
     * @param emailAddress User's e-mail address.
     * @return Authorization URL to redirect the user to.
     */
    public String getAuthorizationUrl(HttpServletRequest request, boolean ignoreState) {
        // Generate an authorization URL based on our client settings,
        // the user's email address, and the state parameter, if present.
        updateSecrets();
        GoogleAuthorizationCodeRequestUrl urlBuilder = new GoogleAuthorizationCodeRequestUrl(
                secrets.getWeb().getClientId(), secrets.getWeb().getRedirectUris().get(0), SCOPES)
                        .setAccessType("offline").setApprovalPrompt("force");
        // Propagate through the current state parameter, so that when the
        // user gets redirected back to our app, they see the file(s) they
        // were originally supposed to see before we realized we weren't
        // authorized.
        if (!ignoreState && request.getParameter("state") != null) {
            urlBuilder.set("state", request.getParameter("state"));
        }

        if (request.getSession() != null) {
            String emailAddress = (String) request.getSession().getAttribute(CredentialMediator.EMAIL_KEY);

            if (emailAddress != null) {
                urlBuilder.set("user_id", emailAddress);
            }
        }

        return urlBuilder.build();
    }

    /**
     * Create a new file given a JSON representation, and return the JSON
     * representation of the created file.
     */
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            CredentialMediator cm = getCredentialMediator(req, resp);
            ClientFile clientFile = new ClientFile(req.getReader());
            //user can initiate a Save from logged out tab, we log out the other users to allow saving any unsaved work
            if (clientFile.userId != null
                    && !clientFile.userId.equals(req.getSession().getAttribute(CredentialMediator.USER_ID_KEY))) {
                endSession(req, resp, cm);
            }

            Drive service = getDriveService(req, resp, cm);

            File file = clientFile.toFile();

            if (!clientFile.content.equals("")) {
                file = service.files()
                        .insert(file, ByteArrayContent.fromString(clientFile.mimeType, clientFile.content))
                        .execute();

            } else {
                file = service.files().insert(file).execute();
            }

            resp.setContentType(JSON_MIMETYPE);
            resp.getWriter().print(new Gson().toJson(file.getId()).toString());
        } catch (Exception e) {
            resp.setStatus(500);
            if (e instanceof GoogleJsonResponseException) {
                GoogleJsonResponseException e2 = (GoogleJsonResponseException) e;
                resp.setStatus(e2.getStatusCode());
            }
            try {
                resp.getWriter().write(getAuthorizationUrl(req, true));
            } catch (Exception ex) {
                throw new RuntimeException("Failed to redirect for authorization.");
            }
        }
    }

    /**
     * Update a file given a JSON representation, and return the JSON
     * representation of the created file.
     */
    @Override
    public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            CredentialMediator cm = getCredentialMediator(req, resp);
            ClientFile clientFile = new ClientFile(req.getReader());
            //user can initiate a Save from logged out tab, we log out the other users to allow saving any unsaved work
            if (clientFile.userId != null
                    && !clientFile.userId.equals(req.getSession().getAttribute(CredentialMediator.USER_ID_KEY))) {
                endSession(req, resp, cm);
            }

            Drive service = getDriveService(req, resp, cm);
            File file = clientFile.toFile();
            file = service.files().update(clientFile.resource_id, file,
                    ByteArrayContent.fromString(clientFile.mimeType, clientFile.content)).execute();

            resp.setContentType(JSON_MIMETYPE);
            resp.getWriter().print(new Gson().toJson(file.getId()).toString());
        } catch (Exception e) {
            resp.setStatus(500);
            if (e instanceof GoogleJsonResponseException) {
                GoogleJsonResponseException e2 = (GoogleJsonResponseException) e;
                resp.setStatus(e2.getStatusCode());
            }
            try {
                resp.getWriter().write(getAuthorizationUrl(req, true));
            } catch (Exception ex) {
                throw new RuntimeException("Failed to redirect for authorization.");
            }
        }
    }

    /**
     * Download the content of the given file.
     *
     * @param service Drive service to use for downloading.
     * @param file File metadata object whose content to download.
     * @return String representation of file content.  String is returned here
     *         because this app is setup for text/plain files.
     * @throws IOException Thrown if the request fails for whatever reason.
     */
    private String downloadFileContent(Drive service, File file) throws IOException {
        GenericUrl url = new GenericUrl(file.getDownloadUrl());
        HttpResponse response = service.getRequestFactory().buildGetRequest(url).execute();
        try {
            return new Scanner(response.getContent(), "UTF-8").useDelimiter("\\A").next();
        } catch (java.util.NoSuchElementException e) {
            return "";
        }
    }

    /**
     * Build and return a Drive service object based on given request parameters.
     *
     * @param req Request to use to fetch code parameter or accessToken session
     *            attribute.
     * @param resp HTTP response to use for redirecting for authorization if
     *             needed.
     * @return Drive service object that is ready to make requests, or null if
     *         there was a problem.
     * @throws IOException 
     */
    private Drive getDriveService(HttpServletRequest req, HttpServletResponse resp, CredentialMediator mediator)
            throws CredentialMediator.NoRefreshTokenException, IOException {
        Credential credentials = mediator.getActiveCredential();

        return Drive.builder(TRANSPORT, JSON_FACTORY).setHttpRequestInitializer(credentials).build();
    }

    /**
     * Loads the secrets.
     */
    protected void updateSecrets() {
        if (secrets == null) {
            try {
                secrets = GoogleClientSecrets.load(JSON_FACTORY, getClientSecretsStream());
            } catch (IOException e) {
                throw new RuntimeException("client_secrets.json is missing or invalid.");
            }
        }
    }

    protected void sendError(HttpServletResponse resp, int code, String message) {
        try {
            resp.sendError(code, message);
        } catch (IOException e) {
            throw new RuntimeException(message);
        }
    }

    protected InputStream getClientSecretsStream() {
        return getServletContext().getResourceAsStream(CLIENT_SECRETS_FILE_PATH);
    }

    protected CredentialMediator getCredentialMediator(HttpServletRequest req, HttpServletResponse resp)
            throws CredentialMediator.NoRefreshTokenException, IOException {
        // Authorize or fetch credentials.  Required here to ensure this happens
        // on first page load.  Then, credentials will be stored in the user's
        // session.
        updateSecrets();
        CredentialMediator mediator = new CredentialMediator(req, resp, secrets);
        mediator.getActiveCredential();
        return mediator;
    }

    protected Credential getCredential(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            CredentialMediator mediator = getCredentialMediator(req, resp);
            return mediator.getActiveCredential();
        } catch (CredentialMediator.NoRefreshTokenException e) {
            throw new RuntimeException("No refresh token found. Re-authorizing.");
        }
    }

    protected String getClientId(HttpServletRequest req, HttpServletResponse resp)
            throws CredentialMediator.NoRefreshTokenException, IOException {
        return getCredentialMediator(req, resp).getClientSecrets().getWeb().getClientId();
    }

    protected void deleteCredential(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            CredentialMediator mediator = getCredentialMediator(req, resp);
            mediator.deleteActiveCredential();
        } catch (CredentialMediator.NoRefreshTokenException e) {
            throw new RuntimeException("Failed to redirect for authorization.");
        }
    }

    protected boolean shouldInvalidateSession(HttpServletRequest request, CredentialMediator mediator) {
        boolean invalidate = false;

        String authCode = request.getParameter("code");
        String sessionUserId = (String) request.getSession().getAttribute(CredentialMediator.USER_ID_KEY);

        if (authCode != null && sessionUserId != null) {
            try {
                Credential cred = mediator.exchangeCode(authCode);
                Userinfo ui = mediator.getUserInfo(cred);
                invalidate = !sessionUserId.equals(ui.getId());
            } catch (NoUserIdException e) {
                e.printStackTrace();
            } catch (CodeExchangeException e) {
                e.printStackTrace();
            }
        }

        return invalidate;
    }

    protected void endSession(HttpServletRequest request, HttpServletResponse response, CredentialMediator mediator)
            throws NoRefreshTokenException, IOException {
        mediator.deleteActiveCredential();
        request.getSession().invalidate();
        Cookie cookie = new Cookie("drive", "");
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        throw new NoRefreshTokenException();
    }
}