/* * 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.data.view; import co.cask.cdap.api.dataset.DatasetDefinition; import co.cask.cdap.api.dataset.DatasetProperties; import co.cask.cdap.api.dataset.table.Table; import co.cask.cdap.common.NotFoundException; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.data2.datafabric.dataset.DatasetsUtil; import co.cask.cdap.data2.dataset2.DatasetFramework; import co.cask.cdap.data2.dataset2.lib.table.MDSKey; import co.cask.cdap.data2.transaction.Transactions; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ViewDetail; import co.cask.cdap.proto.ViewSpecification; import co.cask.tephra.TransactionExecutor; import co.cask.tephra.TransactionExecutorFactory; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.inject.Inject; import java.util.List; /** * Default implementation of {@link ViewStore}. */ public final class MDSViewStore implements ViewStore { private static final Id.DatasetInstance STORE_DATASET_ID = Id.DatasetInstance.from(Id.Namespace.SYSTEM, Constants.AppMetaStore.TABLE); private static final String TYPE_STREAM_VIEW = "stream.view"; private final TransactionExecutorFactory executorFactory; private final DatasetFramework datasetFramework; @Inject public MDSViewStore(final DatasetFramework datasetFramework, TransactionExecutorFactory executorFactory) { this.datasetFramework = datasetFramework; this.executorFactory = executorFactory; } private <T> T execute(TransactionExecutor.Function<ViewMetadataStoreDataset, T> func) { try { Table table = DatasetsUtil.getOrCreateDataset( datasetFramework, STORE_DATASET_ID, "table", DatasetProperties.EMPTY, DatasetDefinition.NO_ARGUMENTS, null); ViewMetadataStoreDataset viewDataset = new ViewMetadataStoreDataset(table); TransactionExecutor txExecutor = Transactions.createTransactionExecutor(executorFactory, viewDataset); return txExecutor.execute(func, viewDataset); } catch (Exception e) { throw new RuntimeException(String.format("Error accessing %s table", Constants.Stream.View.STORE_TABLE), e); } } @Override public boolean createOrUpdate(final Id.Stream.View viewId, final ViewSpecification spec) { return execute(new TransactionExecutor.Function<ViewMetadataStoreDataset, Boolean>() { @Override public Boolean apply(ViewMetadataStoreDataset mds) throws Exception { boolean created = !mds.exists(getKey(viewId)); mds.write(getKey(viewId), new StreamViewEntry(viewId, spec)); return created; } }); } @Override public boolean exists(final Id.Stream.View viewId) { return execute(new TransactionExecutor.Function<ViewMetadataStoreDataset, Boolean>() { @Override public Boolean apply(ViewMetadataStoreDataset mds) throws Exception { return mds.exists(getKey(viewId)); } }); } @Override public void delete(final Id.Stream.View viewId) throws NotFoundException { boolean notFound = execute(new TransactionExecutor.Function<ViewMetadataStoreDataset, Boolean>() { @Override public Boolean apply(ViewMetadataStoreDataset mds) throws Exception { if (!mds.exists(getKey(viewId))) { return true; } mds.deleteAll(getKey(viewId)); return false; } }); if (notFound) { throw new NotFoundException(viewId); } } @Override public List<Id.Stream.View> list(final Id.Stream streamId) { List<StreamViewEntry> entries = execute( new TransactionExecutor.Function<ViewMetadataStoreDataset, List<StreamViewEntry>>() { @Override public List<StreamViewEntry> apply(ViewMetadataStoreDataset mds) throws Exception { return Objects.firstNonNull( mds.<StreamViewEntry>list(getKey(streamId), StreamViewEntry.class), ImmutableList.<StreamViewEntry>of()); } }); ImmutableList.Builder<Id.Stream.View> builder = ImmutableList.builder(); builder.addAll(Collections2.transform(entries, new Function<StreamViewEntry, Id.Stream.View>() { @Override public Id.Stream.View apply(StreamViewEntry input) { return input.getId(); } })); return builder.build(); } @Override public ViewDetail get(final Id.Stream.View viewId) throws NotFoundException { StreamViewEntry entry = execute( new TransactionExecutor.Function<ViewMetadataStoreDataset, StreamViewEntry>() { @Override public StreamViewEntry apply(ViewMetadataStoreDataset mds) throws Exception { if (!mds.exists(getKey(viewId))) { return null; } return mds.get(getKey(viewId), StreamViewEntry.class); } }); if (entry == null) { throw new NotFoundException(viewId); } return new ViewDetail(viewId.getId(), entry.getSpec()); } private MDSKey getKey(Id.Stream id) { return new MDSKey.Builder() .add(TYPE_STREAM_VIEW, id.getNamespaceId(), id.getId()) .build(); } private MDSKey getKey(Id.Stream.View id) { return new MDSKey.Builder() .add(TYPE_STREAM_VIEW, id.getNamespaceId(), id.getStreamId(), id.getId()) .build(); } private static final class StreamViewEntry { private final Id.Stream.View id; private final ViewSpecification spec; private StreamViewEntry(Id.Stream.View id, ViewSpecification spec) { this.id = id; this.spec = spec; } public Id.Stream.View getId() { return id; } public ViewSpecification getSpec() { return spec; } } }