Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions src/main/java/io/split/android/client/Destroyer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package io.split.android.client;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;

import io.split.android.client.factory.FactoryMonitor;
import io.split.android.client.impressions.ImpressionListener;
import io.split.android.client.lifecycle.SplitLifecycleManager;
import io.split.android.client.network.HttpClient;
import io.split.android.client.service.executor.SplitTaskExecutor;
import io.split.android.client.service.synchronizer.SyncManager;
import io.split.android.client.shared.SplitClientContainer;
import io.split.android.client.storage.common.SplitStorageContainer;
import io.split.android.client.telemetry.TelemetrySynchronizer;
import io.split.android.client.utils.logger.Logger;

class Destroyer implements Runnable {

// Lock to prevent concurrent shutdowns and ensure sequence
private final Lock mInitLock;
// Whe
private final AtomicBoolean mCheckClients;
private final SplitClientContainer mClientContainer;
private final SplitStorageContainer mStorageContainer;
private final long mInitStartTime;
private final TelemetrySynchronizer mTelemetrySynchronizer;
private final ExecutorService mImpressionsLoggingTaskExecutor;
private final ExecutorService mImpressionsObserverExecutor;
private final SyncManager mSyncManager;
private final SplitLifecycleManager mLifecycleManager;
private final FactoryMonitor mFactoryMonitor;
private final String mApiKey;
private final ImpressionListener mCustomerImpressionListener;
private final HttpClient mDefaultHttpClient;
private final SplitManager mSplitManager;
private final SplitTaskExecutor mSplitTaskExecutor;
private final SplitTaskExecutor mSplitSingleThreadTaskExecutor;
private final AtomicBoolean mIsTerminated;

Destroyer(
Lock initLock,
AtomicBoolean checkClients,
SplitClientContainer clientContainer,
SplitStorageContainer storageContainer,
long initStartTime,
TelemetrySynchronizer telemetrySynchronizer,
ExecutorService impressionsLoggingTaskExecutor,
ExecutorService impressionsObserverExecutor,
SyncManager syncManager,
SplitLifecycleManager lifecycleManager,
FactoryMonitor factoryMonitor,
String apiKey,
ImpressionListener customerImpressionListener,
HttpClient defaultHttpClient,
SplitManager splitManager,
SplitTaskExecutor splitTaskExecutor,
SplitTaskExecutor splitSingleThreadTaskExecutor,
AtomicBoolean isTerminated
) {
mInitLock = initLock;
mCheckClients = checkClients;
mClientContainer = clientContainer;
mStorageContainer = storageContainer;
mInitStartTime = initStartTime;
mTelemetrySynchronizer = telemetrySynchronizer;
mImpressionsLoggingTaskExecutor = impressionsLoggingTaskExecutor;
mImpressionsObserverExecutor = impressionsObserverExecutor;
mSyncManager = syncManager;
mLifecycleManager = lifecycleManager;
mFactoryMonitor = factoryMonitor;
mApiKey = apiKey;
mCustomerImpressionListener = customerImpressionListener;
mDefaultHttpClient = defaultHttpClient;
mSplitManager = splitManager;
mSplitTaskExecutor = splitTaskExecutor;
mSplitSingleThreadTaskExecutor = splitSingleThreadTaskExecutor;
mIsTerminated = isTerminated;
}

@Override
public void run() {
mInitLock.lock();
try {
if (mCheckClients.get() && !mClientContainer.getAll().isEmpty()) {
Logger.d("Avoiding shutdown due to active clients");
return;
}
Logger.w("Shutdown called for split");
mStorageContainer
.getTelemetryStorage()
.recordSessionLength(
System.currentTimeMillis() - mInitStartTime
);
mTelemetrySynchronizer.flush();
mTelemetrySynchronizer.destroy();
Logger.d("Successful shutdown of telemetry");
mImpressionsLoggingTaskExecutor.shutdown();
mImpressionsObserverExecutor.shutdown();
Logger.d("Successful shutdown of impressions logging executor");
mSyncManager.stop();
Logger.d("Flushing impressions and events");
mLifecycleManager.destroy();
mClientContainer.destroy();
Logger.d("Successful shutdown of lifecycle manager");
mFactoryMonitor.remove(mApiKey);
Logger.d("Successful shutdown of segment fetchers");
mCustomerImpressionListener.close();
Logger.d("Successful shutdown of ImpressionListener");
mDefaultHttpClient.close();
Logger.d("Successful shutdown of httpclient");
mSplitManager.destroy();
Logger.d("Successful shutdown of manager");
mSplitTaskExecutor.stop();
mSplitSingleThreadTaskExecutor.stop();
Logger.d("Successful shutdown of task executor");
mStorageContainer.getAttributesStorageContainer().destroy();
Logger.d("Successful shutdown of attributes storage");
mIsTerminated.set(true);
Logger.d("SplitFactory has been destroyed");
} catch (Exception e) {
Logger.e(e, "We could not shutdown split");
} finally {
mCheckClients.set(false);
mInitLock.unlock();
}
}
}
82 changes: 28 additions & 54 deletions src/main/java/io/split/android/client/SplitFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public class SplitFactoryImpl implements SplitFactory {
private final Key mDefaultClientKey;
private final SplitManager mManager;
private final Runnable mDestroyer;
private boolean mIsTerminated = false;
private final AtomicBoolean mIsTerminated = new AtomicBoolean(false);
private final AtomicBoolean mCheckClients = new AtomicBoolean(false);
private final String mApiKey;

Expand Down Expand Up @@ -302,58 +302,6 @@ private SplitFactoryImpl(@NonNull String apiToken, @NonNull Key key, @NonNull Sp
streamingComponents.getPushNotificationManager(), componentsRegister, workManagerWrapper,
mEventsTrackerProvider, flagSetsFilter, splitParser);


mDestroyer = new Runnable() {
public void run() {
mInitLock.lock();
try {
if (mCheckClients.get() && !mClientContainer.getAll().isEmpty()) {
Logger.d("Avoiding shutdown due to active clients");
return;
}
Logger.w("Shutdown called for split");
mStorageContainer.getTelemetryStorage().recordSessionLength(System.currentTimeMillis() - initializationStartTime);
telemetrySynchronizer.flush();
telemetrySynchronizer.destroy();
Logger.d("Successful shutdown of telemetry");
impressionsLoggingTaskExecutor.shutdown();
impressionsObserverExecutor.shutdown();
Logger.d("Successful shutdown of impressions logging executor");
mSyncManager.stop();
Logger.d("Flushing impressions and events");
mLifecycleManager.destroy();
mClientContainer.destroy();
Logger.d("Successful shutdown of lifecycle manager");
mFactoryMonitor.remove(mApiKey);
Logger.d("Successful shutdown of segment fetchers");
customerImpressionListener.close();
Logger.d("Successful shutdown of ImpressionListener");
defaultHttpClient.close();
Logger.d("Successful shutdown of httpclient");
mManager.destroy();
Logger.d("Successful shutdown of manager");
mSplitTaskExecutor.stop();
splitSingleThreadTaskExecutor.stop();
Logger.d("Successful shutdown of task executor");
mStorageContainer.getAttributesStorageContainer().destroy();
Logger.d("Successful shutdown of attributes storage");
mIsTerminated = true;
Logger.d("SplitFactory has been destroyed");
} catch (Exception e) {
Logger.e(e, "We could not shutdown split");
} finally {
mCheckClients.set(false);
mInitLock.unlock();
}
}
};
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Using the full path to avoid conflicting with Thread.destroy()
SplitFactoryImpl.this.destroy();
}
});
// Set up async initialization
final SplitFactoryHelper.Initializer initializer = new SplitFactoryHelper.Initializer(apiToken,
config,
Expand Down Expand Up @@ -385,6 +333,32 @@ public void run() {
mManager = new SplitManagerImpl(
mStorageContainer.getSplitsStorage(),
new SplitValidatorImpl(), splitParser);
mDestroyer = new Destroyer(
mInitLock,
mCheckClients,
mClientContainer,
mStorageContainer,
initializationStartTime,
telemetrySynchronizer,
impressionsLoggingTaskExecutor,
impressionsObserverExecutor,
mSyncManager,
mLifecycleManager,
mFactoryMonitor,
mApiKey,
customerImpressionListener,
defaultHttpClient,
mManager,
mSplitTaskExecutor,
splitSingleThreadTaskExecutor,
mIsTerminated);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Using the full path to avoid conflicting with Thread.destroy()
SplitFactoryImpl.this.destroy();
}
});
}

private static String getFlagsSpec(@Nullable TestingConfig testingConfig) {
Expand Down Expand Up @@ -423,7 +397,7 @@ public SplitManager manager() {
@Override
public void destroy() {
synchronized (SplitFactoryImpl.class) {
if (!mIsTerminated) {
if (!mIsTerminated.get()) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.schedule(mDestroyer, 100, TimeUnit.MILLISECONDS);
executor.schedule(new Runnable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class FallbacksSanitizerImpl implements FallbacksSanitizer {
private static final String TREATMENT_REGEXP = "^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$";
private static final Pattern TREATMENT_PATTERN = Pattern.compile(TREATMENT_REGEXP);



@Override
@Nullable
public FallbackTreatment sanitizeGlobal(@Nullable FallbackTreatment global) {
Expand Down
Loading
Loading