/*
* 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.data2.registry;
import co.cask.cdap.api.dataset.DatasetManagementException;
import co.cask.cdap.api.dataset.DatasetProperties;
import co.cask.cdap.api.dataset.table.Table;
import co.cask.cdap.data2.datafabric.dataset.DatasetProvider;
import co.cask.cdap.data2.dataset2.DatasetFramework;
import co.cask.cdap.data2.dataset2.tx.Transactional;
import co.cask.cdap.proto.Id;
import co.cask.tephra.TransactionExecutor;
import co.cask.tephra.TransactionExecutorFactory;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterators;
import com.google.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
/**
* Store program -> dataset/stream usage information.
*
* TODO: Reduce duplication between this and {@link UsageDataset}.
*/
public class UsageRegistry {
private static final Logger LOG = LoggerFactory.getLogger(UsageRegistry.class);
private static final Id.DatasetInstance USAGE_INSTANCE_ID =
Id.DatasetInstance.from(Id.Namespace.SYSTEM, "usage.registry");
private final Transactional<UsageDatasetIterable, UsageDataset> txnl;
@Inject
public UsageRegistry(TransactionExecutorFactory txExecutorFactory,
final DatasetProvider provider) {
txnl = Transactional.of(txExecutorFactory, new Supplier<UsageDatasetIterable>() {
@Override
public UsageDatasetIterable get() {
try {
Object usageDataset = Preconditions.checkNotNull(
provider.getOrCreate(USAGE_INSTANCE_ID, UsageDataset.class.getSimpleName(),
DatasetProperties.EMPTY, null, null),
"Couldn't get/create usage registry dataset");
// Backward compatible check for version <= 3.0.0
if (usageDataset instanceof UsageDataset) {
return new UsageDatasetIterable((UsageDataset) usageDataset);
}
return new UsageDatasetIterable(new UsageDataset((Table) usageDataset));
} catch (Exception e) {
LOG.error("Failed to access usage table", e);
throw Throwables.propagate(e);
}
}
});
}
/**
* Registers usage of a stream by multiple ids.
*
* @param users the users of the stream
* @param streamId the stream
*/
public void registerAll(final Iterable<? extends Id> users, final Id.Stream streamId) {
txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Void>() {
@Override
public Void apply(UsageDatasetIterable input) throws Exception {
for (Id user : users) {
// TODO: CDAP-2251: remove redundancy
if (user instanceof Id.Program) {
register((Id.Program) user, streamId);
}
}
return null;
}
});
}
/**
* Register usage of a stream by an id.
*
* @param user the user of the stream
* @param streamId the stream
*/
public void register(Id user, Id.Stream streamId) {
registerAll(Collections.singleton(user), streamId);
}
/**
* Registers usage of a dataset by multiple ids.
*
* @param users the users of the dataset
* @param datasetId the dataset
*/
public void registerAll(final Iterable<? extends Id> users, final Id.DatasetInstance datasetId) {
txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Void>() {
@Override
public Void apply(UsageDatasetIterable input) throws Exception {
for (Id user : users) {
// TODO: CDAP-2251: remove redundancy
if (user instanceof Id.Program) {
register((Id.Program) user, datasetId);
}
}
return null;
}
});
}
/**
* Registers usage of a dataset by multiple ids.
*
* @param user the user of the dataset
* @param datasetId the dataset
*/
public void register(Id user, Id.DatasetInstance datasetId) {
registerAll(Collections.singleton(user), datasetId);
}
/**
* Registers usage of a dataset by a program.
*
* @param programId program
* @param datasetInstanceId dataset
*/
public void register(final Id.Program programId, final Id.DatasetInstance datasetInstanceId) {
txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Void>() {
@Override
public Void apply(UsageDatasetIterable input) throws Exception {
input.getUsageDataset().register(programId, datasetInstanceId);
return null;
}
});
}
/**
* Registers usage of a stream by a program.
*
* @param programId program
* @param streamId stream
*/
public void register(final Id.Program programId, final Id.Stream streamId) {
txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Void>() {
@Override
public Void apply(UsageDatasetIterable input) throws Exception {
input.getUsageDataset().register(programId, streamId);
return null;
}
});
}
/**
* Unregisters all usage information of an application.
*
* @param applicationId application
*/
public void unregister(final Id.Application applicationId) {
txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Void>() {
@Override
public Void apply(UsageDatasetIterable input) throws Exception {
input.getUsageDataset().unregister(applicationId);
return null;
}
});
}
public Set<Id.DatasetInstance> getDatasets(final Id.Application id) {
return txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Set<Id.DatasetInstance>>() {
@Override
public Set<Id.DatasetInstance> apply(UsageDatasetIterable input) throws Exception {
return input.getUsageDataset().getDatasets(id);
}
});
}
public Set<Id.Stream> getStreams(final Id.Application id) {
return txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Set<Id.Stream>>() {
@Override
public Set<Id.Stream> apply(UsageDatasetIterable input) throws Exception {
return input.getUsageDataset().getStreams(id);
}
});
}
public Set<Id.DatasetInstance> getDatasets(final Id.Program id) {
return txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Set<Id.DatasetInstance>>() {
@Override
public Set<Id.DatasetInstance> apply(UsageDatasetIterable input) throws Exception {
return input.getUsageDataset().getDatasets(id);
}
});
}
public Set<Id.Stream> getStreams(final Id.Program id) {
return txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Set<Id.Stream>>() {
@Override
public Set<Id.Stream> apply(UsageDatasetIterable input) throws Exception {
return input.getUsageDataset().getStreams(id);
}
});
}
public Set<Id.Program> getPrograms(final Id.Stream id) {
return txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Set<Id.Program>>() {
@Override
public Set<Id.Program> apply(UsageDatasetIterable input) throws Exception {
return input.getUsageDataset().getPrograms(id);
}
});
}
public Set<Id.Program> getPrograms(final Id.DatasetInstance id) {
return txnl.executeUnchecked(new TransactionExecutor.Function<UsageDatasetIterable, Set<Id.Program>>() {
@Override
public Set<Id.Program> apply(UsageDatasetIterable input) throws Exception {
return input.getUsageDataset().getPrograms(id);
}
});
}
/**
* For passing {@link UsageDataset} to {@link Transactional#of}.
*/
public static final class UsageDatasetIterable implements Iterable<UsageDataset> {
private final UsageDataset usageDataset;
private UsageDatasetIterable(UsageDataset usageDataset) {
this.usageDataset = usageDataset;
}
public UsageDataset getUsageDataset() {
return usageDataset;
}
@Override
public Iterator<UsageDataset> iterator() {
return Iterators.singletonIterator(usageDataset);
}
}
/**
* Adds datasets and types to the given {@link DatasetFramework} used by usage registry.
*
* @param datasetFramework framework to add types and datasets to
*/
public static void setupDatasets(DatasetFramework datasetFramework) throws IOException, DatasetManagementException {
datasetFramework.addInstance(Table.class.getName(), USAGE_INSTANCE_ID, DatasetProperties.EMPTY);
}
}