co.cask.cdap.data2.datafabric.dataset.service.DatasetServiceTestBase.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.data2.datafabric.dataset.service.DatasetServiceTestBase.java

Source

/*
 * Copyright  2014-2016 Cask Data, Inc.
 *
 * 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 co.cask.cdap.data2.datafabric.dataset.service;

import co.cask.cdap.api.dataset.module.DatasetDefinitionRegistry;
import co.cask.cdap.api.dataset.module.DatasetModule;
import co.cask.cdap.api.metrics.MetricsCollectionService;
import co.cask.cdap.common.conf.CConfiguration;
import co.cask.cdap.common.conf.CConfigurationUtil;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.guice.ConfigModule;
import co.cask.cdap.common.guice.LocationRuntimeModule;
import co.cask.cdap.common.io.Locations;
import co.cask.cdap.common.metrics.NoOpMetricsCollectionService;
import co.cask.cdap.common.namespace.NamespacedLocationFactory;
import co.cask.cdap.common.utils.DirUtils;
import co.cask.cdap.data.dataset.SystemDatasetInstantiatorFactory;
import co.cask.cdap.data.runtime.SystemDatasetRuntimeModule;
import co.cask.cdap.data2.datafabric.dataset.DatasetMetaTableUtil;
import co.cask.cdap.data2.datafabric.dataset.RemoteDatasetFramework;
import co.cask.cdap.data2.datafabric.dataset.instance.DatasetInstanceManager;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetAdminOpHTTPHandler;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetAdminService;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetOpExecutorService;
import co.cask.cdap.data2.datafabric.dataset.service.executor.InMemoryDatasetOpExecutor;
import co.cask.cdap.data2.datafabric.dataset.service.mds.MDSDatasetsRegistry;
import co.cask.cdap.data2.datafabric.dataset.type.DatasetTypeManager;
import co.cask.cdap.data2.dataset2.DatasetDefinitionRegistryFactory;
import co.cask.cdap.data2.dataset2.DefaultDatasetDefinitionRegistry;
import co.cask.cdap.data2.dataset2.InMemoryDatasetFramework;
import co.cask.cdap.data2.dataset2.InMemoryNamespaceStore;
import co.cask.cdap.data2.metadata.store.NoOpMetadataStore;
import co.cask.cdap.data2.metrics.DatasetMetricsReporter;
import co.cask.cdap.data2.transaction.DelegatingTransactionSystemClientService;
import co.cask.cdap.data2.transaction.TransactionSystemClientService;
import co.cask.cdap.explore.client.DiscoveryExploreClient;
import co.cask.cdap.explore.client.ExploreFacade;
import co.cask.cdap.internal.test.AppJarHelper;
import co.cask.cdap.proto.DatasetModuleMeta;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.NamespaceMeta;
import co.cask.cdap.store.NamespaceStore;
import co.cask.common.http.HttpRequest;
import co.cask.common.http.HttpRequests;
import co.cask.common.http.HttpResponse;
import co.cask.common.http.ObjectResponse;
import co.cask.http.HttpHandler;
import co.cask.tephra.TransactionExecutorFactory;
import co.cask.tephra.TransactionManager;
import co.cask.tephra.inmemory.InMemoryTxSystemClient;
import co.cask.tephra.runtime.TransactionInMemoryModule;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.twill.common.Threads;
import org.apache.twill.discovery.InMemoryDiscoveryService;
import org.apache.twill.discovery.ServiceDiscovered;
import org.apache.twill.filesystem.LocalLocationFactory;
import org.apache.twill.filesystem.Location;
import org.apache.twill.filesystem.LocationFactory;
import org.apache.twill.internal.Services;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Base class for unit-tests that require running of {@link DatasetService}
 */
public abstract class DatasetServiceTestBase {
    private InMemoryDiscoveryService discoveryService;
    private DatasetOpExecutorService opExecutorService;
    private DatasetService service;
    private LocationFactory locationFactory;
    private NamespaceStore namespaceStore;
    protected TransactionManager txManager;
    protected RemoteDatasetFramework dsFramework;
    protected InMemoryDatasetFramework inMemoryDatasetFramework;

    private int port = -1;

    @ClassRule
    public static TemporaryFolder tmpFolder = new TemporaryFolder();

    @Before
    public void before() throws Exception {
        CConfiguration cConf = CConfiguration.create();
        File dataDir = new File(tmpFolder.newFolder(), "data");
        cConf.set(Constants.CFG_LOCAL_DATA_DIR, dataDir.getAbsolutePath());
        if (!DirUtils.mkdirs(dataDir)) {
            throw new RuntimeException(String.format("Could not create DatasetFramework output dir %s", dataDir));
        }
        cConf.set(Constants.Dataset.Manager.OUTPUT_DIR, dataDir.getAbsolutePath());
        cConf.set(Constants.Dataset.Manager.ADDRESS, "localhost");
        cConf.setBoolean(Constants.Dangerous.UNRECOVERABLE_RESET, true);

        // Starting DatasetService service
        discoveryService = new InMemoryDiscoveryService();
        MetricsCollectionService metricsCollectionService = new NoOpMetricsCollectionService();

        // Tx Manager to support working with datasets
        Configuration txConf = HBaseConfiguration.create();
        CConfigurationUtil.copyTxProperties(cConf, txConf);
        txManager = new TransactionManager(txConf);
        txManager.startAndWait();
        InMemoryTxSystemClient txSystemClient = new InMemoryTxSystemClient(txManager);
        TransactionSystemClientService txSystemClientService = new DelegatingTransactionSystemClientService(
                txSystemClient);

        final Injector injector = Guice.createInjector(new ConfigModule(cConf),
                new LocationRuntimeModule().getInMemoryModules(),
                new SystemDatasetRuntimeModule().getInMemoryModules(), new TransactionInMemoryModule());

        DatasetDefinitionRegistryFactory registryFactory = new DatasetDefinitionRegistryFactory() {
            @Override
            public DatasetDefinitionRegistry create() {
                DefaultDatasetDefinitionRegistry registry = new DefaultDatasetDefinitionRegistry();
                injector.injectMembers(registry);
                return registry;
            }
        };

        locationFactory = injector.getInstance(LocationFactory.class);
        NamespacedLocationFactory namespacedLocationFactory = injector.getInstance(NamespacedLocationFactory.class);
        dsFramework = new RemoteDatasetFramework(cConf, discoveryService, registryFactory);
        SystemDatasetInstantiatorFactory datasetInstantiatorFactory = new SystemDatasetInstantiatorFactory(
                locationFactory, dsFramework, cConf);

        DatasetAdminService datasetAdminService = new DatasetAdminService(dsFramework, cConf, locationFactory,
                datasetInstantiatorFactory, new NoOpMetadataStore());
        ImmutableSet<HttpHandler> handlers = ImmutableSet
                .<HttpHandler>of(new DatasetAdminOpHTTPHandler(datasetAdminService));
        opExecutorService = new DatasetOpExecutorService(cConf, discoveryService, metricsCollectionService,
                handlers);

        opExecutorService.startAndWait();

        ImmutableMap<String, DatasetModule> modules = ImmutableMap.<String, DatasetModule>builder()
                .putAll(injector.getInstance(Key.get(new TypeLiteral<Map<String, DatasetModule>>() {
                }, Names.named("defaultDatasetModules")))).putAll(DatasetMetaTableUtil.getModules()).build();

        TransactionExecutorFactory txExecutorFactory = injector.getInstance(TransactionExecutorFactory.class);

        inMemoryDatasetFramework = new InMemoryDatasetFramework(registryFactory, modules, cConf);
        MDSDatasetsRegistry mdsDatasetsRegistry = new MDSDatasetsRegistry(txSystemClientService,
                inMemoryDatasetFramework);

        ExploreFacade exploreFacade = new ExploreFacade(new DiscoveryExploreClient(cConf, discoveryService), cConf);
        namespaceStore = new InMemoryNamespaceStore();
        namespaceStore.create(NamespaceMeta.DEFAULT);
        DatasetInstanceService instanceService = new DatasetInstanceService(
                new DatasetTypeManager(cConf, mdsDatasetsRegistry, locationFactory,
                        // we don't need any default modules in this test
                        Collections.<String, DatasetModule>emptyMap()),
                new DatasetInstanceManager(mdsDatasetsRegistry), new InMemoryDatasetOpExecutor(dsFramework),
                exploreFacade, cConf, txExecutorFactory, registryFactory, namespaceStore);

        service = new DatasetService(cConf, namespacedLocationFactory, discoveryService, discoveryService,
                new DatasetTypeManager(cConf, mdsDatasetsRegistry, locationFactory,
                        // we don't need any default modules in this test
                        Collections.<String, DatasetModule>emptyMap()),
                metricsCollectionService, new InMemoryDatasetOpExecutor(dsFramework), mdsDatasetsRegistry,
                new HashSet<DatasetMetricsReporter>(), instanceService,
                new LocalStorageProviderNamespaceAdmin(cConf, namespacedLocationFactory, exploreFacade),
                namespaceStore);

        // Start dataset service, wait for it to be discoverable
        service.start();
        final CountDownLatch startLatch = new CountDownLatch(1);
        discoveryService.discover(Constants.Service.DATASET_MANAGER)
                .watchChanges(new ServiceDiscovered.ChangeListener() {
                    @Override
                    public void onChange(ServiceDiscovered serviceDiscovered) {
                        if (!Iterables.isEmpty(serviceDiscovered)) {
                            startLatch.countDown();
                        }
                    }
                }, Threads.SAME_THREAD_EXECUTOR);

        startLatch.await(5, TimeUnit.SECONDS);
        // this usually happens while creating a namespace, however not doing that in data fabric tests
        Locations.mkdirsIfNotExists(namespacedLocationFactory.get(Id.Namespace.DEFAULT));
    }

    @After
    public void after() throws Exception {
        Services.chainStop(service, opExecutorService, txManager);
        namespaceStore.delete(Id.Namespace.DEFAULT);
        Locations.deleteQuietly(locationFactory.create(Id.Namespace.DEFAULT.getId()));
    }

    private synchronized int getPort() {
        int attempts = 0;
        while (port < 0 && attempts++ < 10) {
            ServiceDiscovered discovered = discoveryService.discover(Constants.Service.DATASET_MANAGER);
            if (!discovered.iterator().hasNext()) {
                Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
                continue;
            }
            port = discovered.iterator().next().getSocketAddress().getPort();
        }

        return port;
    }

    protected URL getUrl(String path) throws MalformedURLException {
        return getUrl(Id.Namespace.DEFAULT.getId(), path);
    }

    protected URL getUrl(String namespace, String path) throws MalformedURLException {
        return new URL(String.format("http://localhost:%d/%s/namespaces/%s%s", getPort(),
                Constants.Gateway.API_VERSION_3_TOKEN, namespace, path));
    }

    protected URL getStorageProviderNamespaceAdminUrl(String namespace, String operation)
            throws MalformedURLException {
        String resource = String.format("%s/namespaces/%s/data/admin/%s", Constants.Gateway.API_VERSION_3,
                namespace, operation);
        return new URL("http://" + "localhost" + ":" + getPort() + resource);
    }

    protected Location createModuleJar(Class moduleClass, Location... bundleEmbeddedJars) throws IOException {
        LocationFactory lf = new LocalLocationFactory(tmpFolder.newFolder());
        File[] embeddedJars = new File[bundleEmbeddedJars.length];
        for (int i = 0; i < bundleEmbeddedJars.length; i++) {
            File file = tmpFolder.newFile();
            Files.copy(Locations.newInputSupplier(bundleEmbeddedJars[i]), file);
            embeddedJars[i] = file;
        }

        return AppJarHelper.createDeploymentJar(lf, moduleClass, embeddedJars);
    }

    protected HttpResponse deployModule(String moduleName, Class moduleClass) throws Exception {
        return deployModule(Id.DatasetModule.from(Id.Namespace.DEFAULT, moduleName), moduleClass);
    }

    protected HttpResponse deployModule(Id.DatasetModule module, Class moduleClass) throws Exception {
        Location moduleJar = createModuleJar(moduleClass);
        HttpRequest request = HttpRequest.put(getUrl(module.getNamespaceId(), "/data/modules/" + module.getId()))
                .addHeader("X-Class-Name", moduleClass.getName()).withBody(Locations.newInputSupplier(moduleJar))
                .build();
        return HttpRequests.execute(request);
    }

    // creates a bundled jar with moduleClass and list of bundleEmbeddedJar files, moduleName and moduleClassName are
    // used to make request for deploying module.
    protected int deployModuleBundled(String moduleName, String moduleClassName, Class moduleClass,
            Location... bundleEmbeddedJars) throws IOException {
        Location moduleJar = createModuleJar(moduleClass, bundleEmbeddedJars);
        HttpRequest request = HttpRequest.put(getUrl("/data/modules/" + moduleName))
                .addHeader("X-Class-Name", moduleClassName).withBody(Locations.newInputSupplier(moduleJar)).build();
        return HttpRequests.execute(request).getResponseCode();
    }

    protected ObjectResponse<List<DatasetModuleMeta>> getModules() throws IOException {
        return getModules(Id.Namespace.DEFAULT);
    }

    protected ObjectResponse<List<DatasetModuleMeta>> getModules(Id.Namespace namespace) throws IOException {
        return ObjectResponse.fromJsonBody(makeModulesRequest(namespace), new TypeToken<List<DatasetModuleMeta>>() {
        }.getType());
    }

    protected HttpResponse makeModulesRequest(Id.Namespace namespaceId) throws IOException {
        HttpRequest request = HttpRequest.get(getUrl(namespaceId.getId(), "/data/modules")).build();
        return HttpRequests.execute(request);
    }

    protected HttpResponse deleteModule(String moduleName) throws Exception {
        return deleteModule(Id.DatasetModule.from(Id.Namespace.DEFAULT, moduleName));
    }

    protected HttpResponse deleteModule(Id.DatasetModule module) throws Exception {
        return HttpRequests.execute(
                HttpRequest.delete(getUrl(module.getNamespaceId(), "/data/modules/" + module.getId())).build());
    }

    protected HttpResponse deleteModules() throws IOException {
        return deleteModules(Id.Namespace.DEFAULT);
    }

    protected HttpResponse deleteModules(Id.Namespace namespace) throws IOException {
        return HttpRequests.execute(HttpRequest.delete(getUrl(namespace.getId(), "/data/modules/")).build());
    }

    protected void assertNamespaceNotFound(HttpResponse response, Id.Namespace namespaceId) {
        Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getResponseCode());
        Assert.assertTrue(response.getResponseBodyAsString().contains(namespaceId.toString()));
    }
}