/*
* ApplicationInsights-Java
* Copyright (c) Microsoft Corporation
* All rights reserved.
*
* MIT License
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the ""Software""), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.microsoft.applicationinsights.internal.channel.common;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import com.microsoft.applicationinsights.internal.channel.TransmissionDispatcher;
import com.microsoft.applicationinsights.internal.channel.TransmissionsLoader;
import com.microsoft.applicationinsights.internal.logger.InternalLogger;
import com.google.common.base.Preconditions;
import com.microsoft.applicationinsights.internal.shutdown.Stoppable;
/**
* The class is responsible for loading transmission files that were saved to the disk
*
* The class will ask for the oldest transmission file and will hand it to the dispatcher
*
* Created by gupele on 12/22/2014.
*/
public final class ActiveTransmissionLoader implements TransmissionsLoader {
public final static int MAX_THREADS_ALLOWED = 10;
private final static int DEFAULT_NUMBER_OF_THREADS = 1;
private final static long DEFAULT_SLEEP_INTERVAL_WHEN_NO_TRANSMISSIONS_FOUND_IN_MILLS = 2000;
private final static long DEFAULT_SLEEP_INTERVAL_AFTER_DISPATCHING_IN_MILLS = 100;
// The helper class that encapsulates the file system access
private final TransmissionFileSystemOutput fileSystem;
// A synchronized flag to let us know when to stop
private final AtomicBoolean done = new AtomicBoolean(false);
// The dispatcher is needed to process the fetched Transmissions
private final TransmissionDispatcher dispatcher;
private CyclicBarrier barrier;
private final TransmissionPolicyStateFetcher transmissionPolicyFetcher;
// The threads that do the work
private final Thread[] threads;
private final long sleepIntervalWhenNoTransmissionsFoundInMills;
public ActiveTransmissionLoader(TransmissionFileSystemOutput fileSystem, TransmissionPolicyStateFetcher transmissionPolicy, TransmissionDispatcher dispatcher) {
this(fileSystem, dispatcher, transmissionPolicy, DEFAULT_NUMBER_OF_THREADS);
}
public ActiveTransmissionLoader(final TransmissionFileSystemOutput fileSystem,
final TransmissionDispatcher dispatcher,
final TransmissionPolicyStateFetcher transmissionPolicy,
int numberOfThreads) {
Preconditions.checkNotNull(fileSystem, "fileSystem must be a non-null value");
Preconditions.checkNotNull(dispatcher, "dispatcher must be a non-null value");
Preconditions.checkNotNull(transmissionPolicy, "transmissionPolicy must be a non-null value");
Preconditions.checkArgument(numberOfThreads > 0, "numberOfThreads must be a positive number");
Preconditions.checkArgument(numberOfThreads < MAX_THREADS_ALLOWED, "numberOfThreads must be smaller than %s", MAX_THREADS_ALLOWED);
// Guy: This will probably be changed once we have configuration
this.sleepIntervalWhenNoTransmissionsFoundInMills = DEFAULT_SLEEP_INTERVAL_WHEN_NO_TRANSMISSIONS_FOUND_IN_MILLS;
this.transmissionPolicyFetcher = transmissionPolicy;
this.fileSystem = fileSystem;
this.dispatcher = dispatcher;
threads = new Thread[numberOfThreads];
for (int i = 0; i < numberOfThreads; ++i) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
try {
barrier.await();
} catch (InterruptedException e) {
InternalLogger.INSTANCE.error("Interrupted during barrier wait, exception: %s", e.getMessage());
} catch (BrokenBarrierException e) {
InternalLogger.INSTANCE.error("Failed during barrier wait, exception: %s", e.getMessage());
}
// Avoid un-expected exit of threads
while (!done.get()) {
try {
TransmissionPolicy currentTransmissionState = transmissionPolicyFetcher.getCurrentState();
switch (currentTransmissionState) {
case UNBLOCKED:
fetchNext(true);
break;
case BLOCKED_BUT_CAN_BE_PERSISTED:
Thread.sleep(DEFAULT_SLEEP_INTERVAL_AFTER_DISPATCHING_IN_MILLS);
break;
case BLOCKED_AND_CANNOT_BE_PERSISTED:
// We fetch but don't do anything with the Transmission
// which means that we are cleaning the disk as needed by that policy
fetchNext(false);
break;
default:
InternalLogger.INSTANCE.error("Could not find transmission policy '%s'", currentTransmissionState);
Thread.sleep(DEFAULT_SLEEP_INTERVAL_AFTER_DISPATCHING_IN_MILLS);
break;
}
} catch (Exception e) {
} catch (Throwable t) {
}
// TODO: check whether we need to pause after exception
}
}
});
threads[i].setDaemon(true);
}}
@Override
public synchronized boolean load(boolean waitForThreadsToStart) {
if (barrier == null) {
int numberOfThreads = threads.length;
if (waitForThreadsToStart) {
++numberOfThreads;
}
barrier = new CyclicBarrier(numberOfThreads);
}
for (Thread thread : threads) {
thread.start();
}
try {
barrier.await();
return true;
} catch (InterruptedException e) {
InternalLogger.INSTANCE.error("Interrupted during barrier wait, exception: %s", e.getMessage());
} catch (BrokenBarrierException e) {
InternalLogger.INSTANCE.error("Failed during barrier wait, exception: %s", e.getMessage());
}
return false;
}
@Override
public void stop(long timeout, TimeUnit timeUnit) {
done.set(true);
for (Thread thread : threads) {
try {
thread.interrupt();
thread.join();
} catch (InterruptedException e) {
InternalLogger.INSTANCE.error("Interrupted during join of active transmission loader, exception: %s", e.getMessage());
}
}
}
private void fetchNext(boolean shouldDispatch) throws InterruptedException {
Transmission transmission = fileSystem.fetchOldestFile();
if (transmission == null) {
Thread.sleep(sleepIntervalWhenNoTransmissionsFoundInMills);
} else {
if (shouldDispatch) {
dispatcher.dispatch(transmission);
}
// TODO: check if we need this as configuration value
Thread.sleep(DEFAULT_SLEEP_INTERVAL_AFTER_DISPATCHING_IN_MILLS);
}
}
}