Package com.indeed.proctor.store

Source Code of com.indeed.proctor.store.FileBasedProctorStore$ProctorUpdater

package com.indeed.proctor.store;

import com.google.common.collect.Maps;
import com.indeed.util.varexport.Export;
import com.indeed.proctor.common.Serializers;
import com.indeed.proctor.common.model.TestDefinition;
import com.indeed.proctor.common.model.TestMatrixDefinition;
import com.indeed.proctor.common.model.TestMatrixVersion;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
* @author ketan
* @author parker
*/
public abstract class FileBasedProctorStore implements ProctorStore {
    private static final Logger LOGGER = Logger.getLogger(FileBasedProctorStore.class);
    private static final String SUFFIX = ".json";
    static final String TEST_DEFINITIONS_DIRECTORY = "test-definitions";
    static final String TEST_METADATA_FILENAME = "metadata" + SUFFIX;
    static final String TEST_DEFINITION_FILENAME = "definition" + SUFFIX;
    final ObjectMapper objectMapper = Serializers.strict();

    protected final FileBasedPersisterCore core;

    protected FileBasedProctorStore(FileBasedPersisterCore core) {
        this.core = core;
    }

    /**
     * @return true if the file has changed
     */
    protected static <T> boolean writeIfChanged(final ObjectMapper mapper, final File f, final T newThing) throws StoreException.TestUpdateException {
        if (f.exists()) {
            try {
                final T currentThing = (T) mapper.readValue(f, newThing.getClass());
                if (currentThing.equals(newThing)) {
                    return false;
                }
            } catch (final JsonParseException e) {
                throw new StoreException.TestUpdateException("Unable to parse instance of " + newThing.getClass().getCanonicalName() + " from " + f, e);
            } catch (final JsonMappingException e) {
                throw new StoreException.TestUpdateException("Unable to parse instance of " + newThing.getClass().getCanonicalName() + " from " + f, e);
            } catch (final IOException e) {
                throw new StoreException.TestUpdateException("Unable to parse instance of " + newThing.getClass().getCanonicalName() + " from " + f, e);
            }
        }
        FileBasedProctorStore.writeThing(mapper, f, newThing);
        return true;
    }

    protected static <T> void writeThing(final ObjectMapper mapper, final File f, final T newThing) throws StoreException.TestUpdateException {
        try {
            mapper.defaultPrettyPrintingWriter().writeValue(f, newThing);
        } catch (final JsonGenerationException e) {
            throw new StoreException.TestUpdateException("Unable to write instance of " + newThing.getClass().getCanonicalName() + " to " + f, e);
        } catch (final JsonMappingException e) {
            throw new StoreException.TestUpdateException("Unable to write instance of " + newThing.getClass().getCanonicalName() + " to " + f, e);
        } catch (final IOException e) {
            throw new StoreException.TestUpdateException("Unable to write instance of " + newThing.getClass().getCanonicalName() + " to " + f, e);
        }
    }

    static File getTestDefinitionDirectory(final String testName, File workingDir) {
        return new File(workingDir + File.separator + TEST_DEFINITIONS_DIRECTORY + File.separator + testName);
    }

    @Override
    public TestMatrixVersion getCurrentTestMatrix() throws StoreException {
        return getTestMatrix(this.getLatestVersion());
    }

    @Export(name="core", doc="")
    public FileBasedPersisterCore getCore() {
        return core;
    }

    @Override
    public final TestMatrixVersion getTestMatrix(final String fetchRevision) throws StoreException {
        long start = System.currentTimeMillis();
        final TestVersionResult result = core.determineVersions(fetchRevision);
        if(LOGGER.isDebugEnabled()) {
            final long elapsed = System.currentTimeMillis() - start;
            LOGGER.debug(String.format("Took %d ms to identify %d potential tests", elapsed, result.getTests().size()));
        }
        if(result == null) {
            LOGGER.error("Unable to determine tests for " + core.toString());
            return null;
        }
        TestMatrixVersion tmv = new TestMatrixVersion();

        final Map<String, TestDefinition> testDefinitions = Maps.newLinkedHashMap();
        start = System.currentTimeMillis();
        for (final TestVersionResult.Test testDefFile : result.getTests()) {
            final long startForTest = System.currentTimeMillis();
            final TestDefinition testDefinition = getTestDefinition(testDefFile.getTestName(), testDefFile.getRevision());
            if(LOGGER.isTraceEnabled()) {
                final long elapsed = System.currentTimeMillis() - startForTest;
                LOGGER.debug(String.format("Took %d ms to load %s (r%s) %s", elapsed, testDefFile.getTestName(), testDefFile.getRevision(), testDefinition == null ? "unsuccessfully" : "successfully"));
            }
            if(testDefinition == null) {
                LOGGER.info("Returning null TestMatrix because " + testDefFile.getTestName() + " returned null test-definition.");
                return null;
            }
            testDefinitions.put(testDefFile.getTestName(), testDefinition);
        }
        if(LOGGER.isDebugEnabled()) {
            final long elapsed = System.currentTimeMillis() - start;
            LOGGER.debug(String.format("Took %d ms to load all %d tests", elapsed, testDefinitions.size()));
        }

        final TestMatrixDefinition tmd = new TestMatrixDefinition();
        tmd.setTests(testDefinitions);

        tmv.setTestMatrixDefinition(tmd);

        tmv.setPublished(result.getPublished());
        tmv.setAuthor(result.getAuthor());
        tmv.setVersion(result.getVersion());
        tmv.setDescription(result.getDescription());

        return tmv;
    }

    @Override
    public TestDefinition getCurrentTestDefinition(final String testName) throws StoreException {
        // Get the first test history
        final List<Revision> tdvList = this.getHistory(testName, 0, 1);
        if(tdvList.size() == 1) {
            final Revision tdv = tdvList.get(0);
            return getTestDefinition(testName, tdv.getRevision());
        } else {
            LOGGER.info("Not history returned for " + testName + ", returning null");
            return null;
        }
    }

    @Override
    public TestDefinition getTestDefinition(final String testName, String fetchRevision) throws StoreException {
        try {
            return getFileContents(TestDefinition.class, new String[] { TEST_DEFINITIONS_DIRECTORY, testName, TEST_DEFINITION_FILENAME }, null, fetchRevision);
        } catch (final JsonProcessingException e) {
            throw new StoreException(String.format("Unable to deserialize JSON for %s r%s", testName, fetchRevision), e);
        } catch (final StoreException.ReadException e) {
            throw e;
        }
    }

    public void shutdown() {
        try {
            close();
        } catch (IOException e) {
            LOGGER.error("Ignored exception during closing", e);
        }
    }

    @Override
    public void close() throws IOException {
        core.close();
    }

    private final <C> C getFileContents(Class<C> c, String[] path, C defaultValue, String revision) throws StoreException.ReadException, JsonProcessingException {
        return core.getFileContents(c, path, defaultValue, revision);
    }

    protected final <T> boolean updateThing(final FileBasedProctorStore.RcsClient rcsClient, final File file, final T thing) throws Exception {
        final boolean thingExisted = file.exists();
        final boolean thingChanged = FileBasedProctorStore.writeIfChanged(objectMapper, file, thing);
        if (!thingExisted || rcsClient.getRevisionControlType().equals("git")) {
            rcsClient.add(file);
        }
        return thingChanged;
    }

    @Override
    public final void updateTestDefinition(final String username, final String password, final String previousVersion, final String testName, final TestDefinition testDefinition, final Map<String, String> metadata, final String comment) throws StoreException.TestUpdateException {
        LOGGER.info(String.format("Update Test Definition: %s %s r%s", username, testName, previousVersion));
        core.doInWorkingDirectory(username, password, comment, previousVersion, new ProctorUpdater() {
            @Override
            public boolean doInWorkingDirectory(final RcsClient rcsClient, final File workingDir) throws Exception {
                final File testDefinitionDirectory = getTestDefinitionDirectory(testName, workingDir);
                final File testDefinitionFile = new File(testDefinitionDirectory + File.separator + TEST_DEFINITION_FILENAME);
                final File metaDataFile = new File(testDefinitionDirectory + File.separator + TEST_METADATA_FILENAME);
                if (!testDefinitionFile.exists()) {
                    throw new StoreException.TestUpdateException("Attempting to update non-existent test " + testName);
                }

                //  this is easier than trying to get svnKit to do a useful diff
                boolean thingsChanged = updateThing(rcsClient, testDefinitionFile, testDefinition);
                thingsChanged = updateThing(rcsClient, metaDataFile, metadata) || thingsChanged;
                return thingsChanged;
            }
        });
    }

    @Override
    public final void addTestDefinition(final String username, final String password, final String testName, final TestDefinition testDefinition, final Map<String, String> metadata, final String comment) throws StoreException.TestUpdateException {
        LOGGER.info(String.format("Add Test Definition: %s %s", username, testName));
        core.doInWorkingDirectory(username, password, comment, core.getAddTestRevision(), new ProctorUpdater() {
            @Override
            public boolean doInWorkingDirectory(final RcsClient rcsClient, final File workingDir) throws Exception {
                final File testDefinitionDirectory = getTestDefinitionDirectory(testName, workingDir);
                final File testDefinitionFile = new File(testDefinitionDirectory + File.separator + TEST_DEFINITION_FILENAME);
                final File metaDataFile = new File(testDefinitionDirectory + File.separator + TEST_METADATA_FILENAME);

                if (testDefinitionFile.exists() || metaDataFile.exists()) {
                    throw new StoreException.TestUpdateException("Supposedly new test '" + testName + "' already exists");
                }

                testDefinitionDirectory.mkdirs();

                writeThing(objectMapper, testDefinitionFile, testDefinition);
                rcsClient.add(testDefinitionFile);

                writeThing(objectMapper, metaDataFile, metadata);
                rcsClient.add(metaDataFile);

                return true;
            }
        });
    }

    @Override
    public final void deleteTestDefinition(final String username, final String password, final String previousVersion, final String testName, final TestDefinition testDefinition, final String comment)
            throws StoreException.TestUpdateException {
        LOGGER.info(String.format("Delete Test Definition: %s %s r%s ", username, testName, previousVersion));
        core.doInWorkingDirectory(username, password, comment, previousVersion, new ProctorUpdater() {
            @Override
            public boolean doInWorkingDirectory(final RcsClient rcsClient, final File workingDir) throws Exception {
                final File testDefinitionDirectory = getTestDefinitionDirectory(testName, workingDir);

                if (!testDefinitionDirectory.exists()) {
                    throw new StoreException.TestUpdateException("Unable to delete non-existent test " + testName);
                }
                rcsClient.delete(testDefinitionDirectory);

                return true;
            }
        });
    }

    public interface ProctorUpdater {
        boolean doInWorkingDirectory(FileBasedProctorStore.RcsClient rcsClient, File workingDir) throws Exception;
    }

    public interface RcsClient {
        void add(File file) throws Exception;

        void delete(File testDefinitionDirectory) throws Exception;

        String getRevisionControlType();
    }

}
TOP

Related Classes of com.indeed.proctor.store.FileBasedProctorStore$ProctorUpdater

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.