1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.data.measurement; 18 19 20 import com.android.adservices.LogUtil; 21 import com.android.internal.annotations.VisibleForTesting; 22 23 import java.util.Optional; 24 25 /** 26 * Abstract class for Datastore management. 27 */ 28 public abstract class DatastoreManager { 29 30 /** 31 * Consumer interface for Dao operations. 32 */ 33 @FunctionalInterface 34 public interface ThrowingCheckedConsumer { 35 /** 36 * Performs the operation on {@link IMeasurementDao}. 37 */ accept(IMeasurementDao measurementDao)38 void accept(IMeasurementDao measurementDao) throws DatastoreException; 39 } 40 41 /** 42 * Function interface for Dao operations that returns {@link Output}. 43 * 44 * @param <Output> output type 45 */ 46 @FunctionalInterface 47 public interface ThrowingCheckedFunction<Output> { 48 /** 49 * Performs the operation on Dao. 50 * 51 * @return Output result of the operation 52 */ apply(IMeasurementDao measurementDao)53 Output apply(IMeasurementDao measurementDao) throws DatastoreException; 54 } 55 56 /** 57 * Creates a new transaction object for use in Dao. 58 * 59 * @return transaction 60 */ createNewTransaction()61 protected abstract ITransaction createNewTransaction(); 62 63 /** 64 * Acquire an instance of Dao object for querying the datastore. 65 * 66 * @return Dao object. 67 */ 68 @VisibleForTesting getMeasurementDao()69 public abstract IMeasurementDao getMeasurementDao(); 70 71 /** 72 * Runs the {@code execute} lambda in a transaction. 73 * 74 * @param execute lambda to be executed in a transaction 75 * @param <T> the class for result 76 * @return Optional<T>, empty in case of an error, output otherwise 77 */ runInTransactionWithResult(ThrowingCheckedFunction<T> execute)78 public final <T> Optional<T> runInTransactionWithResult(ThrowingCheckedFunction<T> execute) { 79 IMeasurementDao measurementDao = getMeasurementDao(); 80 ITransaction transaction = createNewTransaction(); 81 if (transaction == null) { 82 return Optional.empty(); 83 } 84 measurementDao.setTransaction(transaction); 85 transaction.begin(); 86 87 Optional<T> result; 88 try { 89 result = Optional.ofNullable(execute.apply(measurementDao)); 90 } catch (DatastoreException ex) { 91 result = Optional.empty(); 92 safePrintDataStoreVersion(); 93 LogUtil.e(ex, "DatastoreException thrown during transaction"); 94 transaction.rollback(); 95 } catch (Exception ex) { 96 // Catch all exceptions for rollback 97 safePrintDataStoreVersion(); 98 LogUtil.e(ex, "Unhandled exception thrown during transaction"); 99 transaction.rollback(); 100 throw ex; 101 } finally { 102 transaction.end(); 103 } 104 105 return result; 106 } 107 108 /** 109 * Runs the {@code execute} lambda in a transaction. 110 * 111 * @param execute lambda to be executed in transaction 112 * @return success true if execution succeeded, false otherwise 113 */ runInTransaction(ThrowingCheckedConsumer execute)114 public final boolean runInTransaction(ThrowingCheckedConsumer execute) { 115 return runInTransactionWithResult((measurementDao) -> { 116 execute.accept(measurementDao); 117 return true; 118 }).orElse(false); 119 } 120 121 /** Prints the underlying data store version catching exceptions it can raise. */ safePrintDataStoreVersion()122 private void safePrintDataStoreVersion() { 123 try { 124 LogUtil.w("Underlying datastore version: " + getDataStoreVersion()); 125 } catch (Exception e) { 126 // If fetching data store version throws an exception, skip printing the DB version. 127 LogUtil.e(e, "Failed to print data store version."); 128 } 129 } 130 131 /** Returns the version the underlying data store is at. E.g. user version of the DB. */ getDataStoreVersion()132 protected abstract int getDataStoreVersion(); 133 } 134