gobblin.runtime.job_catalog.FSJobCatalogHelperTest.java Source code

Java tutorial

Introduction

Here is the source code for gobblin.runtime.job_catalog.FSJobCatalogHelperTest.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 gobblin.runtime.job_catalog;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Semaphore;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

import gobblin.configuration.ConfigurationKeys;
import gobblin.runtime.api.JobSpec;
import gobblin.runtime.job_catalog.FSJobCatalog;
import gobblin.runtime.job_catalog.ImmutableFSJobCatalog;
import gobblin.util.ConfigUtils;
import gobblin.util.PullFileLoader;
import gobblin.util.filesystem.PathAlterationObserverScheduler;
import gobblin.util.filesystem.PathAlterationListener;
import gobblin.util.filesystem.PathAlterationListenerAdaptor;
import gobblin.util.filesystem.PathAlterationObserver;

/**
 * Inherit original Testing for loading configurations, with .properties files.
 * The testing folder structure is:
 * /root
 *  - root.properties
 *  /test1
 *    - test11.pull
 *    - test12.pull
 *    /test11
 *      - test111.pull
 *  /test2
 *    - test.properties
 *    - test21.pull
 * The new testing routine for JobSpec,
 * is to create a jobSpec( Simulated as the result of external JobSpecMonitor)
 * persist it, reload it from file system, and compare with the original JobSpec.
 *
 */

@Test(groups = { "gobblin.runtime" })
public class FSJobCatalogHelperTest {

    // For general type of File system
    private File jobConfigDir;
    private File subDir1;
    private File subDir11;
    private File subDir2;

    private Config sysConfig;
    private PullFileLoader loader;
    private ImmutableFSJobCatalog.JobSpecConverter converter;

    @BeforeClass
    public void setUp() throws IOException {
        this.jobConfigDir = java.nio.file.Files
                .createTempDirectory(String.format("gobblin-test_%s_job-conf", this.getClass().getSimpleName()))
                .toFile();
        FileUtils.forceDeleteOnExit(this.jobConfigDir);
        this.subDir1 = new File(this.jobConfigDir, "test1");
        this.subDir11 = new File(this.subDir1, "test11");
        this.subDir2 = new File(this.jobConfigDir, "test2");

        this.subDir1.mkdirs();
        this.subDir11.mkdirs();
        this.subDir2.mkdirs();

        this.sysConfig = ConfigFactory.parseMap(ImmutableMap.<String, Object>builder()
                .put(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY, this.jobConfigDir.getAbsolutePath())
                .build());
        ImmutableFSJobCatalog.ConfigAccessor cfgAccess = new ImmutableFSJobCatalog.ConfigAccessor(this.sysConfig);
        this.loader = new PullFileLoader(new Path(jobConfigDir.toURI()), FileSystem.get(new Configuration()),
                cfgAccess.getJobConfigurationFileExtensions(), PullFileLoader.DEFAULT_HOCON_PULL_FILE_EXTENSIONS);
        this.converter = new ImmutableFSJobCatalog.JobSpecConverter(new Path(this.jobConfigDir.toURI()),
                Optional.of(FSJobCatalog.CONF_EXTENSION));

        Properties rootProps = new Properties();
        rootProps.setProperty("k1", "a1");
        rootProps.setProperty("k2", "a2");
        // test-job-conf-dir/root.properties
        rootProps.store(new FileWriter(new File(this.jobConfigDir, "root.properties")), "");

        Properties jobProps1 = new Properties();
        jobProps1.setProperty("k1", "c1");
        jobProps1.setProperty("k3", "b3");
        jobProps1.setProperty("k6", "a6");
        // test-job-conf-dir/test1/test11.pull
        jobProps1.store(new FileWriter(new File(this.subDir1, "test11.pull")), "");

        Properties jobProps2 = new Properties();
        jobProps2.setProperty("k7", "a7");
        // test-job-conf-dir/test1/test12.PULL
        jobProps2.store(new FileWriter(new File(this.subDir1, "test12.PULL")), "");

        Properties jobProps3 = new Properties();
        jobProps3.setProperty("k1", "d1");
        jobProps3.setProperty("k8", "a8");
        jobProps3.setProperty("k9", "${k8}");
        // test-job-conf-dir/test1/test11/test111.pull
        jobProps3.store(new FileWriter(new File(this.subDir11, "test111.pull")), "");

        Properties props2 = new Properties();
        props2.setProperty("k2", "b2");
        props2.setProperty("k5", "a5");
        // test-job-conf-dir/test2/test.properties
        props2.store(new FileWriter(new File(this.subDir2, "test.PROPERTIES")), "");

        Properties jobProps4 = new Properties();
        jobProps4.setProperty("k5", "b5");
        // test-job-conf-dir/test2/test21.PULL
        jobProps4.store(new FileWriter(new File(this.subDir2, "test21.PULL")), "");
    }

    // This test doesn't delete framework attributes and
    @Test
    public void testloadGenericJobConfigs() throws ConfigurationException, IOException, URISyntaxException {
        Properties properties = new Properties();
        properties.setProperty(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY,
                this.jobConfigDir.getAbsolutePath());
        List<JobSpec> jobSpecs = Lists.transform(
                Lists.newArrayList(
                        loader.loadPullFilesRecursively(loader.getRootDirectory(), this.sysConfig, false)),
                this.converter);

        List<Properties> jobConfigs = convertJobSpecList2PropList(jobSpecs);
        Assert.assertEquals(jobConfigs.size(), 4);

        // test-job-conf-dir/test1/test11/test111.pull
        Properties jobProps1 = getJobConfigForFile(jobConfigs, "test111.pull");

        //5 is consisting of three attributes, plus ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY
        // which is on purpose to keep
        // plus ConfigurationKeys.JOB_CONFIG_FILE_PATH_KEY, which is not necessary to convert into JobSpec
        // but keep it here to avoid NullPointer exception and validation purpose for testing.
        Assert.assertEquals(jobProps1.stringPropertyNames().size(), 5);
        Assert.assertTrue(jobProps1.containsKey(ConfigurationKeys.JOB_CONFIG_FILE_PATH_KEY));
        Assert.assertEquals(jobProps1.getProperty(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY),
                this.jobConfigDir.getAbsolutePath());
        Assert.assertEquals(jobProps1.getProperty("k1"), "d1");
        Assert.assertEquals(jobProps1.getProperty("k8"), "a8");
        Assert.assertEquals(jobProps1.getProperty("k9"), "a8");

        // test-job-conf-dir/test1/test11.pull
        Properties jobProps2 = getJobConfigForFile(jobConfigs, "test11.pull");
        Assert.assertEquals(jobProps2.stringPropertyNames().size(), 5);
        Assert.assertTrue(jobProps2.containsKey(ConfigurationKeys.JOB_CONFIG_FILE_PATH_KEY));
        Assert.assertEquals(jobProps2.getProperty(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY),
                this.jobConfigDir.getAbsolutePath());
        Assert.assertEquals(jobProps2.getProperty("k1"), "c1");
        Assert.assertEquals(jobProps2.getProperty("k3"), "b3");
        Assert.assertEquals(jobProps2.getProperty("k6"), "a6");

        // test-job-conf-dir/test1/test12.PULL
        Properties jobProps3 = getJobConfigForFile(jobConfigs, "test12.PULL");
        Assert.assertEquals(jobProps3.stringPropertyNames().size(), 3);
        Assert.assertTrue(jobProps3.containsKey(ConfigurationKeys.JOB_CONFIG_FILE_PATH_KEY));
        Assert.assertEquals(jobProps3.getProperty(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY),
                this.jobConfigDir.getAbsolutePath());
        Assert.assertEquals(jobProps3.getProperty("k7"), "a7");

        // test-job-conf-dir/test2/test21.PULL
        Properties jobProps4 = getJobConfigForFile(jobConfigs, "test21.PULL");
        Assert.assertEquals(jobProps4.stringPropertyNames().size(), 3);
        Assert.assertTrue(jobProps4.containsKey(ConfigurationKeys.JOB_CONFIG_FILE_PATH_KEY));
        Assert.assertEquals(jobProps4.getProperty(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY),
                this.jobConfigDir.getAbsolutePath());
        Assert.assertEquals(jobProps4.getProperty("k5"), "b5");
    }

    @Test(dependsOnMethods = { "testloadGenericJobConfigs" })
    public void testloadGenericJobConfig() throws ConfigurationException, IOException {
        Path jobConfigPath = new Path(this.subDir11.getAbsolutePath(), "test111.pull");

        Properties jobProps = ConfigUtils
                .configToProperties(loader.loadPullFile(jobConfigPath, this.sysConfig, false));

        Assert.assertEquals(jobProps.stringPropertyNames().size(), 5);
        Assert.assertEquals(jobProps.getProperty(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY),
                this.jobConfigDir.getAbsolutePath());
        Assert.assertEquals(jobProps.getProperty("k1"), "d1");
        Assert.assertEquals(jobProps.getProperty("k8"), "a8");
        Assert.assertEquals(jobProps.getProperty("k9"), "a8");
    }

    @Test(dependsOnMethods = { "testloadGenericJobConfig" })
    public void testPathAlterationObserver() throws Exception {
        PathAlterationObserverScheduler detector = new PathAlterationObserverScheduler(1000);
        final Set<Path> fileAltered = Sets.newHashSet();
        final Semaphore semaphore = new Semaphore(0);
        PathAlterationListener listener = new PathAlterationListenerAdaptor() {

            @Override
            public void onFileCreate(Path path) {
                fileAltered.add(path);
                semaphore.release();
            }

            @Override
            public void onFileChange(Path path) {
                fileAltered.add(path);
                semaphore.release();
            }
        };

        detector.addPathAlterationObserver(listener, Optional.<PathAlterationObserver>absent(),
                new Path(this.jobConfigDir.getPath()));
        try {
            detector.start();
            // Give the monitor some time to start
            Thread.sleep(1000);

            File jobConfigFile = new File(this.subDir11, "test111.pull");
            Files.touch(jobConfigFile);

            File newJobConfigFile = new File(this.subDir11, "test112.pull");
            Files.append("k1=v1", newJobConfigFile, ConfigurationKeys.DEFAULT_CHARSET_ENCODING);

            semaphore.acquire(2);
            Assert.assertEquals(fileAltered.size(), 2);

            Assert.assertTrue(fileAltered.contains(new Path("file:" + jobConfigFile)));
            Assert.assertTrue(fileAltered.contains(new Path("file:" + newJobConfigFile)));
        } finally {
            detector.stop();
        }
    }

    @AfterClass
    public void tearDown() throws IOException {
        if (this.jobConfigDir != null) {
            FileUtils.forceDelete(this.jobConfigDir);
        }
    }

    private Properties getJobConfigForFile(List<Properties> jobConfigs, String fileName) {
        for (Properties jobConfig : jobConfigs) {
            if (jobConfig.getProperty(ConfigurationKeys.JOB_CONFIG_FILE_PATH_KEY).endsWith(fileName)) {
                return jobConfig;
            }
        }
        return null;
    }

    /**
     * Suppose in the testing routine, each JobSpec will at least have either config or properties.
     * @param jobConfigs
     * @return
     */
    private List<Properties> convertJobSpecList2PropList(List<JobSpec> jobConfigs) {
        List<Properties> result = Lists.newArrayList();
        for (JobSpec js : jobConfigs) {
            Properties propToBeAdded;
            if (js.getConfigAsProperties() != null) {
                propToBeAdded = js.getConfigAsProperties();
            } else {
                propToBeAdded = ConfigUtils.configToProperties(js.getConfig());
            }

            // For the testing purpose, added it back when doing the comparison.
            propToBeAdded.setProperty(ConfigurationKeys.JOB_CONFIG_FILE_PATH_KEY, js.getUri().toString());
            result.add(propToBeAdded);
        }
        return result;
    }
}