org.activityinfo.server.endpoint.gwtrpc.PersistentPolicyProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.activityinfo.server.endpoint.gwtrpc.PersistentPolicyProvider.java

Source

package org.activityinfo.server.endpoint.gwtrpc;

/*
 * #%L
 * ActivityInfo Server
 * %%
 * Copyright (C) 2009 - 2013 UNICEF
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import com.google.common.io.InputSupplier;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
import com.google.inject.Inject;
import org.activityinfo.server.util.blob.BlobService;

import javax.servlet.ServletContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Provides SerializationPolicy from archives in Google Cloud Storage.
 * <p/>
 * <p/>
 * GWT uses a file called STRONGNAME.gwt.rpc to determine which types are
 * allowed to be deserialized on the server. This is to stop bad actors from
 * being able to instantiate whatever java objects they want via GWT-RPC.
 * <p/>
 * <p/>
 * STRONGNAME is the MD5 hash of the compiled javascript, unique to a specific
 * version and browser/language combination.
 * <p/>
 * <p/>
 * Because we use the AppCache to aggressively cache the client on the user's
 * browser, it is very common that when the user opens the client after a few
 * days away, the server has a new version (we are pushing out updates several
 * times a week).
 * <p/>
 * <p/>
 * Unfortunately, this means that the server will not be able to find the
 * permutation's corresponding .gwt.rpc file, and the RPC command will fail with
 * a IncompatibleRemoteException. This forces the user to download the new
 * version of the app before continuing.
 * <p/>
 * <p/>
 * Ideally what we want is the user to be able to use the application while the
 * AppCache fetches the new version in the background.
 * <p/>
 * <p/>
 * To accomplish this, we archive any gwt.rpc file that ever gets requested in
 * Google Cloud Storage, and then check this archive each time a gwt.rpc file is
 * requested. This way, even
 */
public class PersistentPolicyProvider {

    private static final Logger LOGGER = Logger.getLogger(PersistentPolicyProvider.class.getName());

    private ServletContext servletContext;
    private BlobService blobService;

    @Inject
    public PersistentPolicyProvider(BlobService blobService, ServletContext servletContext) {
        this.blobService = blobService;
        this.servletContext = servletContext;
    }

    public SerializationPolicy getSerializationPolicy(String moduleBaseURL, String strongName) {

        LOGGER.info("Loading serialization policy " + strongName);

        SerializationPolicy policy = readFromDeployment(moduleBaseURL, strongName);

        if (policy == null) {
            policy = tryFetchFromBlobService(keyName(strongName));
        }

        if (policy == null) {
            policy = tryFetchFromBlobService(legacyKeyName(strongName));
        }

        return policy;
    }

    private String legacyKeyName(String strongName) {
        return "/gs/activityinfo-gwt-rpc/" + strongName + ".gwt.rpc";
    }

    private SerializationPolicy tryFetchFromBlobService(String key) {
        try {
            LOGGER.fine("Trying to read serialization policy from blobservice at " + key);
            InputSupplier<? extends InputStream> inputSupplier = blobService.get(key);
            InputStream in = inputSupplier.getInput();
            try {
                SerializationPolicy policy = SerializationPolicyLoader.loadFromStream(in, null);

                LOGGER.info("Read serialization policy from blob service at " + key);

                return policy;
            } finally {
                Closeables.closeQuietly(in);
            }

        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Could not load serialization policy from cache", e);
        }
        return null;
    }

    private String keyName(String strongName) {
        return "/gwt-rpc/" + strongName;
    }

    private SerializationPolicy readFromDeployment(String moduleBaseURL, String strongName) {

        // Read the serialization policy from the
        // deployed application files
        byte[] bytes;
        SerializationPolicy policy;

        try {

            String file = deploymentPath(moduleBaseURL, strongName);
            InputStream in = servletContext.getResourceAsStream(file);
            bytes = ByteStreams.toByteArray(in);
            in.close();
            policy = SerializationPolicyLoader.loadFromStream(new ByteArrayInputStream(bytes), null);

            LOGGER.info("Read serialization policy " + strongName + " from deployment");

        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Failed to read serialization policy from deployment", e);
            return null;
        }

        // cache to blob service for later
        try {
            blobService.put(keyName(strongName), ByteStreams.newInputStreamSupplier(bytes));
            LOGGER.info("Cached policy " + strongName + " to blob service");

        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, "Could not cache serialization policy", e);
        }

        return policy;
    }

    private String deploymentPath(String moduleBaseURL, String strongName) throws MalformedURLException {
        String modulePath = new URL(moduleBaseURL).getPath();
        return modulePath + strongName + ".gwt.rpc";
    }
}