com.bigdata.journal.jini.ha.AbstractHA3BackupTestCase.java Source code

Java tutorial

Introduction

Here is the source code for com.bigdata.journal.jini.ha.AbstractHA3BackupTestCase.java

Source

/**
    
Copyright (C) SYSTAP, LLC 2006-2007.  All rights reserved.
    
Contact:
 SYSTAP, LLC
 4501 Tower Road
 Greensboro, NC 27410
 licenses@bigdata.com
    
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; version 2 of the License.
    
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package com.bigdata.journal.jini.ha;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Properties;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import com.bigdata.btree.BytesUtil;
import com.bigdata.ha.HAGlue;
import com.bigdata.ha.msg.HADigestRequest;
import com.bigdata.ha.msg.HARootBlockRequest;
import com.bigdata.journal.CommitCounterUtility;
import com.bigdata.journal.IHABufferStrategy;
import com.bigdata.journal.IRootBlockView;
import com.bigdata.journal.Journal;
import com.bigdata.rdf.sail.webapp.client.ConnectOptions;
import com.bigdata.rdf.sail.webapp.client.RemoteRepository;

/**
 * Abstract base class for testing the {@link ISnapshotPolicy} and
 * {@link IRestorePolicy} that support HA Backup and restore.
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 */
public class AbstractHA3BackupTestCase extends AbstractHA3JournalServerTestCase {

    public AbstractHA3BackupTestCase() {
    }

    public AbstractHA3BackupTestCase(final String name) {
        super(name);
    }

    @Override
    protected int replicationFactor() {

        return 3;

    }

    /**
     * Issue HTTP request to a service to take a snapshot.
     * 
     * @param haGlue
     *            The service.
     *            
     * @throws Exception
     */
    protected void doSnapshotRequest(final HAGlue haGlue) throws Exception {

        doSnapshotRequest(haGlue, null/* percentLogSize */);

    }

    /**
     * Issue HTTP request to a service to take a snapshot.
     * 
     * @param haGlue
     *            The service.
     * @param percentLogSize
     *            The percent log size parameter for the request (optional).
     * 
     * @throws Exception
     */
    protected void doSnapshotRequest(final HAGlue haGlue, final Integer percentLogSize) throws Exception {

        // Client for talking to the NSS.
        final HttpClient httpClient = new DefaultHttpClient(ccm);

        // The NSS service URL (NOT the SPARQL end point).
        final String serviceURL = getNanoSparqlServerURL(haGlue);

        final ConnectOptions opts = new ConnectOptions(
                serviceURL + "/status?snapshot" + (percentLogSize != null ? "=" + percentLogSize : ""));

        opts.method = "GET";

        try {

            final HttpResponse response;

            RemoteRepository.checkResponseCode(response = doConnect(httpClient, opts));

            EntityUtils.consume(response.getEntity());

        } catch (IOException ex) {

            log.error(ex, ex);

            throw ex;

        }

    }

    /**
     * Restore the journal from the specified snapshot on the specified server,
     * applying all available HALog files GT the snapshot and then compare the
     * digest of the restored journal with the digest of the specified server.
     * 
     * @param serverA
     *            NOTE: This ONLY works for serverA. The access to the snapshot
     *            directory for serverA is hardcoded into this method.
     * 
     * @param commitCounterN
     *            The commit counter of the snapshot that will be used to
     *            restore the state of the journal.
     * 
     * @throws IOException
     * @throws InterruptedException 
     * @throws DigestException 
     * @throws NoSuchAlgorithmException 
     */
    protected void doRestoreA(final HAGlue serverA, final long commitCounterN)
            throws IOException, InterruptedException, DigestException, NoSuchAlgorithmException {

        /*
         * The current commit counter on the server. This is the commit point
         * that should be restored.
         */

        final IRootBlockView serverARootBlock = serverA.getRootBlock(new HARootBlockRequest(null/* storeUUID */))
                .getRootBlock();

        final long commitCounterM = serverARootBlock.getCommitCounter();

        final File snapshotFile = SnapshotManager.getSnapshotFile(getSnapshotDirA(), commitCounterN);

        final String basename = CommitCounterUtility.getBaseName(snapshotFile.getName(),
                SnapshotManager.SNAPSHOT_EXT);

        // temporary file in the same directory as the snapshot.
        final File out = File.createTempFile(basename + "-", Journal.Options.JNL,
                snapshotFile.getAbsoluteFile().getParentFile());

        try {

            // Decompress the snapshot.
            SnapshotManager.decompress(snapshotFile, out);

            // Verify that we can open the decompressed file as a Journal.
            {

                final Properties p = new Properties();

                p.setProperty(Journal.Options.FILE, out.getAbsoluteFile().toString());

                Journal jnl = new Journal(p);

                try {

                    // Verify snapshot at the expected commit point.
                    assertEquals(commitCounterN, jnl.getRootBlockView().getCommitCounter());

                    // Verify journal can be dumped without error.
                    dumpJournal(jnl);

                    /*
                     * Now roll that journal forward using the HALog directory.
                     */
                    final HARestore rest = new HARestore(jnl, getHALogDirA());

                    /*
                     * Note: We can not test where we stop at the specified
                     * commit point in this method because the Journal state on
                     * the server could not be used to validate the restored
                     * Journal's state.
                     */
                    rest.restore(false/* listCommitPoints */, Long.MAX_VALUE/* haltingCommitCounter */);

                    /*
                     * FIXME For some reason, we need to close and reopen the
                     * journal before it can be used. See HARestore.
                     */
                    if (true) {
                        jnl.close();

                        // reopen.
                        jnl = new Journal(p);
                    }

                    // Verify can dump journal after restore.
                    dumpJournal(jnl);

                    // Verify journal now at the expected commit point.
                    assertEquals(commitCounterM, jnl.getRootBlockView().getCommitCounter());

                    if (!serverARootBlock.equals(jnl.getRootBlockView())) {
                        fail("Root blocks differ: serverA=" + serverARootBlock + ", restored="
                                + jnl.getRootBlockView());
                    }

                    /*
                     * Compute digest of the restored journal. The digest should
                     * agree with the digest of the Journal on A since we rolled
                     * it forward to the same commit point.
                     */
                    {

                        // digest of A
                        final byte[] digestA = serverA.computeDigest(new HADigestRequest(null/* storeUUID */))
                                .getDigest();

                        final byte[] digest2;
                        {
                            final MessageDigest digest = MessageDigest.getInstance("MD5");

                            // digest of restored journal.
                            ((IHABufferStrategy) (jnl.getBufferStrategy())).computeDigest(null/* snapshot */,
                                    digest);

                            digest2 = digest.digest();
                        }

                        if (!BytesUtil.bytesEqual(digestA, digest2)) {

                            final String digestAStr = new BigInteger(1, digestA).toString(16);

                            final String digest2Str = new BigInteger(1, digest2).toString(16);

                            fail("Digests differ after restore and replay: expected=" + digestAStr + ", actual="
                                    + digest2Str);

                        }

                    }

                } finally {

                    if (jnl != null) {

                        jnl.close();

                    }

                }

            }

        } finally {

            if (out.delete()) {

                log.warn("Could not delete: " + out);

            }

        }

    }

    /**
     * Verify the existence of the snapshot files for the specified commit
     * points and verify that no other snapshot files exist.
     * 
     * @param snapshotDir
     *            The snapshot directory.
     * @param commitCounters
     *            The commit points.
     */
    protected void assertExpectedSnapshots(final File snapshotDir, final long[] commitCounters) {

        for (long commitCounter : commitCounters) {

            final File file = SnapshotManager.getSnapshotFile(snapshotDir, commitCounter);

            if (!file.exists())
                fail("Snapshot not found: " + file);

        }

        assertEquals(commitCounters.length, recursiveCount(snapshotDir, SnapshotManager.SNAPSHOT_FILTER));

    }

    /**
     * We need to set the time at which the {@link DefaultSnapshotPolicy} runs
     * to some point in the future in order to avoid test failures due to
     * violated assumptions when the policy runs up self-triggering (based on
     * the specified run time) during a CI run.
     * <p>
     * We do this by adding one hour to [now] and then converting it into the
     * 'hhmm' format as an integer.
     * 
     * @return The "never run" time as hhmm.
     */
    static protected String getNeverRunSnapshotTime() {

        // Right now.
        final Calendar c = Calendar.getInstance();

        // Plus an hour.
        c.add(Calendar.HOUR_OF_DAY, 1);

        // Get the hour.
        final int hh = c.get(Calendar.HOUR_OF_DAY);

        // And the minutes.
        final int mm = c.get(Calendar.MINUTE);

        // Format as hhmm.
        final String neverRun = "" + hh + (mm < 10 ? "0" : "") + mm;

        return neverRun;

    }

}