/* * Copyright © 2015 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.explore.client; import co.cask.cdap.api.data.format.FormatSpecification; import co.cask.cdap.api.dataset.lib.PartitionKey; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.explore.service.ExploreException; import co.cask.cdap.explore.service.HandleNotFoundException; import co.cask.cdap.explore.service.UnexpectedQueryStatusException; import co.cask.cdap.proto.Id; import com.google.common.base.Throwables; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.SQLException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Explore client facade to be used by streams and datasets. */ public class ExploreFacade { private static final Logger LOG = LoggerFactory.getLogger(ExploreFacade.class); private final ExploreClient exploreClient; private final boolean exploreEnabled; @Inject public ExploreFacade(ExploreClient exploreClient, CConfiguration cConf) { this.exploreClient = exploreClient; this.exploreEnabled = cConf.getBoolean(Constants.Explore.EXPLORE_ENABLED); if (!exploreEnabled) { LOG.warn("Explore functionality for datasets is disabled. All calls to enable explore will be no-ops"); } } /** * Enables ad-hoc exploration of the given stream. * * @param stream id of the stream. * @param tableName name of the Hive table to create. * @param format format of the stream events. */ public void enableExploreStream(Id.Stream stream, String tableName, FormatSpecification format) throws ExploreException, SQLException { if (!exploreEnabled) { return; } ListenableFuture<Void> futureSuccess = exploreClient.enableExploreStream(stream, tableName, format); handleExploreFuture(futureSuccess, "enable", "stream", stream.getId()); } /** * Disables ad-hoc exploration of the given stream. * * @param stream id of the stream. * @param tableName name of the Hive table to delete. */ public void disableExploreStream(Id.Stream stream, String tableName) throws ExploreException, SQLException { if (!exploreEnabled) { return; } ListenableFuture<Void> futureSuccess = exploreClient.disableExploreStream(stream, tableName); handleExploreFuture(futureSuccess, "disable", "stream", stream.getId()); } /** * Enables ad-hoc exploration of the given {@link co.cask.cdap.api.data.batch.RecordScannable}. * @param datasetInstance dataset instance id. */ public void enableExploreDataset(Id.DatasetInstance datasetInstance) throws ExploreException, SQLException { if (!(exploreEnabled && isDatasetExplorable(datasetInstance))) { return; } ListenableFuture<Void> futureSuccess = exploreClient.enableExploreDataset(datasetInstance); handleExploreFuture(futureSuccess, "enable", "dataset", datasetInstance.getId()); } /** * Disable ad-hoc exploration of the given {@link co.cask.cdap.api.data.batch.RecordScannable}. * @param datasetInstance dataset instance id. */ public void disableExploreDataset(Id.DatasetInstance datasetInstance) throws ExploreException, SQLException { if (!(exploreEnabled && isDatasetExplorable(datasetInstance))) { return; } ListenableFuture<Void> futureSuccess = exploreClient.disableExploreDataset(datasetInstance); handleExploreFuture(futureSuccess, "disable", "dataset", datasetInstance.getId()); } public void addPartition(Id.DatasetInstance datasetInstance, PartitionKey key, String location) throws ExploreException, SQLException { if (!exploreEnabled) { return; } ListenableFuture<Void> futureSuccess = exploreClient.addPartition(datasetInstance, key, location); handleExploreFuture(futureSuccess, "add", "partition", datasetInstance.getId()); } public void dropPartition(Id.DatasetInstance datasetInstance, PartitionKey key) throws ExploreException, SQLException { if (!exploreEnabled) { return; } ListenableFuture<Void> futureSuccess = exploreClient.dropPartition(datasetInstance, key); handleExploreFuture(futureSuccess, "drop", "partition", datasetInstance.getId()); } public void createNamespace(Id.Namespace namespace) throws ExploreException, SQLException { if (!exploreEnabled) { return; } ListenableFuture<ExploreExecutionResult> futureSuccess = exploreClient.addNamespace(namespace); handleExploreFuture(futureSuccess, "add", "namespace", namespace.getId()); } public void removeNamespace(Id.Namespace namespace) throws ExploreException, SQLException { if (!exploreEnabled) { return; } ListenableFuture<ExploreExecutionResult> futureSuccess = exploreClient.removeNamespace(namespace); handleExploreFuture(futureSuccess, "remove", "namespace", namespace.getId()); } private boolean isDatasetExplorable(Id.DatasetInstance datasetInstance) { return !Id.Namespace.SYSTEM.equals(datasetInstance.getNamespace()); } // wait for the enable/disable operation to finish and log and throw exceptions as appropriate if there was an error. private void handleExploreFuture(ListenableFuture future, String operation, String type, String name) throws ExploreException, SQLException { try { future.get(20, TimeUnit.SECONDS); } catch (InterruptedException e) { LOG.error("Caught exception", e); Thread.currentThread().interrupt(); } catch (ExecutionException e) { Throwable t = Throwables.getRootCause(e); if (t instanceof ExploreException) { LOG.error("{} explore did not finish successfully for {} instance {}.", operation, type, name); throw (ExploreException) t; } else if (t instanceof SQLException) { throw (SQLException) t; } else if (t instanceof HandleNotFoundException) { // Cannot happen unless explore server restarted, or someone calls close in between. LOG.error("Error running {} explore", operation, e); throw Throwables.propagate(e); } else if (t instanceof UnexpectedQueryStatusException) { UnexpectedQueryStatusException sE = (UnexpectedQueryStatusException) t; LOG.error("{} explore operation ended in an unexpected state - {}", operation, sE.getStatus().name(), e); throw Throwables.propagate(e); } } catch (TimeoutException e) { LOG.error("Error running {} explore - operation timed out", operation, e); throw Throwables.propagate(e); } } }