co.mitro.analysis.StatsGenerator.java Source code

Java tutorial

Introduction

Here is the source code for co.mitro.analysis.StatsGenerator.java

Source

/*******************************************************************************
 * Copyright (c) 2013, 2014 Lectorius, Inc.
 * Authors:
 * Vijay Pandurangan (vijayp@mitro.co)
 * Evan Jones (ej@mitro.co)
 * Adam Hilss (ahilss@mitro.co)
 *
 *
 *     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/>.
 *     
 *     You can contact the authors at inbound@mitro.co.
 *******************************************************************************/
package co.mitro.analysis;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

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

import co.mitro.core.exceptions.DoEmailVerificationException;
import co.mitro.core.exceptions.MitroServletException;
import co.mitro.core.server.Manager;
import co.mitro.core.server.Templates;
import co.mitro.core.server.data.DBAcl;
import co.mitro.core.server.data.DBGroup;
import co.mitro.core.server.data.DBHistoricalOrgState;
import co.mitro.core.server.data.DBHistoricalUserState;
import co.mitro.core.server.data.DBIdentity;
import co.mitro.core.server.data.RPC.GetOrganizationStateResponse;
import co.mitro.core.server.data.RPC.ListMySecretsAndGroupKeysResponse;
import co.mitro.core.server.data.RPC.ListMySecretsAndGroupKeysResponse.GroupInfo;
import co.mitro.core.servlets.GetOrganizationState;
import co.mitro.core.servlets.ListMySecretsAndGroupKeys;
import co.mitro.core.servlets.MitroServlet.MitroRequestContext;

import com.github.mustachejava.Mustache;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultimap;
import com.google.common.io.Files;

public class StatsGenerator {
    private static final Mustache userStateTemplate = Templates.compile("user_state.mustache");
    private static final Mustache orgStateTemplate = Templates.compile("org_state.mustache");
    private static final Mustache indexTemplate = Templates.compile("index_state.mustache");
    private static final Logger logger = LoggerFactory.getLogger(StatsGenerator.class);

    public static final class Link implements Comparable<Object> {
        public String text;
        public String url;
        public int count;

        public Link(String t, String u, int count) {
            text = t;
            url = u;
            this.count = count;
        }

        @Override
        public int compareTo(Object o) {
            Link l = (Link) o;
            return (text + url).compareTo(l.text + l.url);
        }
    }

    public static class Snapshot {
        public final ArrayList<DBHistoricalOrgState> orgStateObjects = new ArrayList<>();
        public final ArrayList<DBHistoricalUserState> userStateObjects = new ArrayList<>();
    }

    /**
     * Generate statistics and return newly created objects that have not been committed.
     * @param outDir directory in which to write summary files. Subdirectories outDir/users 
     *        and outDir/orgs must exist. Supply null for no output.
     */
    public static Snapshot generateStatistics(String outDir, Manager manager)
            throws SQLException, IOException, MitroServletException {
        final long runTimestampMs = System.currentTimeMillis();
        Snapshot output = new Snapshot();

        // TODO: don't do this in one gigantic transaction.
        Multimap<Integer, Link> countToFile = TreeMultimap.create(Ordering.natural().reverse(), Ordering.natural());
        // get all orgs.
        Map<Integer, GroupInfo> orgIdToOrg = Maps.newHashMap();
        for (DBGroup o : DBGroup.getAllOrganizations(manager)) {
            GroupInfo newGi = new GroupInfo();
            newGi.autoDelete = o.isAutoDelete();
            newGi.groupId = o.getId();
            newGi.isTopLevelOrg = true;
            newGi.name = o.getName();
            Set<String> users = Sets.newHashSet();
            for (DBGroup orgGroup : o.getAllOrgGroups(manager)) {
                users.add(orgGroup.getName());
            }
            newGi.users = Lists.newArrayList(users);
            orgIdToOrg.put(newGi.groupId, newGi);
        }
        int numPeople = 0;
        for (DBIdentity id : manager.identityDao.queryForAll()) {
            ++numPeople;
            try {
                logger.info(id.getName() + ": " + id.getGuidCookie());
                DBHistoricalUserState userState = getHistoricalUserState(manager, runTimestampMs, orgIdToOrg, id);
                output.userStateObjects.add(userState);

                String filename = id.getName() + ".html";
                renderIfOutputEnabled(outDir, "/users/" + filename, userStateTemplate, userState);
                countToFile.put(userState.numSecrets, new Link(id.getName(), filename, userState.numSecrets));
            } catch (MitroServletException e) {
                logger.error("UNKNOWN ERROR", e);
            }
        }
        renderIfOutputEnabled(outDir, "/users/index.html", indexTemplate, countToFile.values());

        countToFile.clear();
        int numOrgs = 0;
        // now do the orgs
        for (DBGroup org : DBGroup.getAllOrganizations(manager)) {
            ++numOrgs;
            // hack to make this work
            Set<Integer> admins = Sets.newHashSet();
            org.putDirectUsersIntoSet(admins, DBAcl.adminAccess());
            int userId = admins.iterator().next();
            DBIdentity dbi = manager.identityDao.queryForId(userId);
            MitroRequestContext context = new MitroRequestContext(dbi, null, manager, null);
            GetOrganizationStateResponse resp = GetOrganizationState.doOperation(context, org.getId());
            DBHistoricalOrgState orgState = new DBHistoricalOrgState(resp, org.getId(), runTimestampMs);
            output.orgStateObjects.add(orgState);

            String filename = org.getId() + ".html";
            renderIfOutputEnabled(outDir, "/orgs/" + filename, orgStateTemplate, orgState);
            countToFile.put(orgState.numMembers + orgState.numAdmins, new Link(org.getName() + org.getId(),
                    org.getId() + ".html", orgState.numAdmins + orgState.numMembers));
        }
        renderIfOutputEnabled(outDir, "/orgs/index.html", indexTemplate, countToFile.values());
        renderIfOutputEnabled(outDir, "/index.html", indexTemplate,
                ImmutableList.of(new Link("organizations", "orgs/index.html", numOrgs),
                        new Link("users", "users/index.html", numPeople)));

        return output;
    }

    public static DBHistoricalUserState getHistoricalUserState(Manager manager, final long runTimestampMs,
            Map<Integer, GroupInfo> orgIdToOrg, DBIdentity id)
            throws SQLException, MitroServletException, DoEmailVerificationException {
        MitroRequestContext context = new MitroRequestContext(id, null, manager, null);
        ListMySecretsAndGroupKeysResponse out = ListMySecretsAndGroupKeys.executeWithoutAuditLog(context);
        DBHistoricalUserState userState = new DBHistoricalUserState(out, orgIdToOrg, runTimestampMs);
        userState.referrerDomain = id.getReferrer();
        return userState;
    }

    /**
     * Renders template with scope to a file located at outDir + suffix.
     *
     * @param outDir must not end with /
     * @param suffix must start with /
     * @param template
     * @param scope
     */
    private static void renderIfOutputEnabled(String outDir, String suffix, Mustache template, Object scope)
            throws IOException {
        assert suffix.charAt(0) == '/';
        if (outDir != null) {
            assert !outDir.endsWith("/");
            String path = outDir + suffix;
            try (BufferedWriter writer = Files.newWriter(new File(path), Charsets.UTF_8)) {
                template.execute(writer, scope);
            }
        }
    }
}