com.google.gerrit.pgm.ProtobufImport.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.pgm.ProtobufImport.java

Source

// Copyright (C) 2015 The Android Open Source Project
//
// 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.google.gerrit.pgm;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;

import com.google.auto.value.AutoValue;
import com.google.common.collect.Iterables;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.pgm.util.RuntimeShutdown;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gwtorm.protobuf.CodecFactory;
import com.google.gwtorm.protobuf.ProtobufCodec;
import com.google.gwtorm.schema.RelationModel;
import com.google.gwtorm.schema.java.JavaSchemaModel;
import com.google.gwtorm.server.Access;
import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.protobuf.ByteString;
import com.google.protobuf.Parser;
import com.google.protobuf.UnknownFieldSet;

import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.kohsuke.args4j.Option;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Import data from a protocol buffer dump into the database.
 * <p>
 * Takes as input a file containing protocol buffers concatenated together with
 * varint length encoding, as in {@link Parser#parseDelimitedFrom(InputStream)}.
 * Each message contains a single field with a tag corresponding to the relation
 * ID in the {@link com.google.gwtorm.server.Relation} annotation.
 * <p>
 * <strong>Warning</strong>: This method blindly upserts data into the database.
 * It should only be used to restore a protobuf-formatted backup into a new,
 * empty site.
 */
public class ProtobufImport extends SiteProgram {
    @Option(name = "--file", aliases = { "-f" }, required = true, metaVar = "FILE", usage = "File to import from")
    private File file;

    private final LifecycleManager manager = new LifecycleManager();
    private final Map<Integer, Relation> relations = new HashMap<>();

    @Inject
    private SchemaFactory<ReviewDb> schemaFactory;

    @Override
    public int run() throws Exception {
        mustHaveValidSite();

        Injector dbInjector = createDbInjector(SINGLE_USER);
        manager.add(dbInjector);
        manager.start();
        RuntimeShutdown.add(new Runnable() {
            @Override
            public void run() {
                manager.stop();
            }
        });
        dbInjector.injectMembers(this);

        ProgressMonitor progress = new TextProgressMonitor();
        progress.beginTask("Importing entities", ProgressMonitor.UNKNOWN);
        try (ReviewDb db = schemaFactory.open()) {
            for (RelationModel model : new JavaSchemaModel(ReviewDb.class).getRelations()) {
                relations.put(model.getRelationID(), Relation.create(model, db));
            }

            Parser<UnknownFieldSet> parser = UnknownFieldSet.getDefaultInstance().getParserForType();
            try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
                UnknownFieldSet msg;
                while ((msg = parser.parseDelimitedFrom(in)) != null) {
                    Map.Entry<Integer, UnknownFieldSet.Field> e = Iterables.getOnlyElement(msg.asMap().entrySet());
                    Relation rel = checkNotNull(relations.get(e.getKey()), "unknown relation ID %s in message: %s",
                            e.getKey(), msg);
                    List<ByteString> values = e.getValue().getLengthDelimitedList();
                    checkState(values.size() == 1, "expected one string field in message: %s", msg);
                    upsert(rel, values.get(0));
                    progress.update(1);
                }
            }
            progress.endTask();
        }

        return 0;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static void upsert(Relation rel, ByteString s) throws OrmException {
        Collection ents = Collections.singleton(rel.codec().decode(s));
        try {
            // Not all relations support update; fall back manually.
            rel.access().insert(ents);
        } catch (OrmDuplicateKeyException e) {
            rel.access().delete(ents);
            rel.access().insert(ents);
        }
    }

    @AutoValue
    abstract static class Relation {
        private static Relation create(RelationModel model, ReviewDb db) throws IllegalAccessException,
                InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
            Method m = db.getClass().getMethod(model.getMethodName());
            Class<?> clazz = Class.forName(model.getEntityTypeClassName());
            return new AutoValue_ProtobufImport_Relation((Access<?, ?>) m.invoke(db), CodecFactory.encoder(clazz));
        }

        abstract Access<?, ?> access();

        abstract ProtobufCodec<?> codec();
    }
}