All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.launchdarkly.sdk.server.interfaces.DataSourceStatusProvider Maven / Gradle / Ivy

There is a newer version: 7.5.0
Show newest version
package com.launchdarkly.sdk.server.interfaces;

import com.google.common.base.Strings;

import java.time.Duration;
import java.time.Instant;
import java.util.Objects;

/**
 * An interface for querying the status of a {@link DataSource}. The data source is the component
 * that receives updates to feature flag data; normally this is a streaming connection, but it could
 * be polling or file data depending on your configuration.
 * 

* An implementation of this interface is returned by {@link com.launchdarkly.sdk.server.interfaces.LDClientInterface#getDataSourceStatusProvider}. * Application code never needs to implement this interface. * * @since 5.0.0 */ public interface DataSourceStatusProvider { /** * Returns the current status of the data source. *

* All of the built-in data source implementations are guaranteed to update this status whenever they * successfully initialize, encounter an error, or recover after an error. *

* For a custom data source implementation, it is the responsibility of the data source to report its * status via {@link DataSourceUpdates}; if it does not do so, the status will always be reported as * {@link State#INITIALIZING}. * * @return the latest status; will never be null */ public Status getStatus(); /** * Subscribes for notifications of status changes. *

* The listener will be notified whenever any property of the status has changed. See {@link Status} for an * explanation of the meaning of each property and what could cause it to change. *

* Notifications will be dispatched on a worker thread. It is the listener's responsibility to return as soon as * possible so as not to block subsequent notifications. * * @param listener the listener to add */ public void addStatusListener(StatusListener listener); /** * Unsubscribes from notifications of status changes. * * @param listener the listener to remove; if no such listener was added, this does nothing */ public void removeStatusListener(StatusListener listener); /** * A synchronous method for waiting for a desired connection state. *

* If the current state is already {@code desiredState} when this method is called, it immediately returns. * Otherwise, it blocks until 1. the state has become {@code desiredState}, 2. the state has become * {@link State#OFF} (since that is a permanent condition), 3. the specified timeout elapses, or 4. * the current thread is deliberately interrupted with {@link Thread#interrupt()}. *

* A scenario in which this might be useful is if you want to create the {@code LDClient} without waiting * for it to initialize, and then wait for initialization at a later time or on a different thread: *


   *     // create the client but do not wait
   *     LDConfig config = new LDConfig.Builder().startWait(Duration.ZERO).build();
   *     client = new LDClient(sdkKey, config);
   *     
   *     // later, possibly on another thread:
   *     boolean inited = client.getDataSourceStatusProvider().waitFor(
   *         DataSourceStatusProvider.State.VALID, Duration.ofSeconds(10));
   *     if (!inited) {
   *         // do whatever is appropriate if initialization has timed out
   *     }       
   * 
* * @param desiredState the desired connection state (normally this would be {@link State#VALID}) * @param timeout the maximum amount of time to wait-- or {@link Duration#ZERO} to block indefinitely * (unless the thread is explicitly interrupted) * @return true if the connection is now in the desired state; false if it timed out, or if the state * changed to {@link State#OFF} and that was not the desired state * @throws InterruptedException if {@link Thread#interrupt()} was called on this thread while blocked */ public boolean waitFor(State desiredState, Duration timeout) throws InterruptedException; /** * An enumeration of possible values for {@link DataSourceStatusProvider.Status#getState()}. */ public enum State { /** * The initial state of the data source when the SDK is being initialized. *

* If it encounters an error that requires it to retry initialization, the state will remain at * {@link #INITIALIZING} until it either succeeds and becomes {@link #VALID}, or permanently fails and * becomes {@link #OFF}. */ INITIALIZING, /** * Indicates that the data source is currently operational and has not had any problems since the * last time it received data. *

* In streaming mode, this means that there is currently an open stream connection and that at least * one initial message has been received on the stream. In polling mode, it means that the last poll * request succeeded. */ VALID, /** * Indicates that the data source encountered an error that it will attempt to recover from. *

* In streaming mode, this means that the stream connection failed, or had to be dropped due to some * other error, and will be retried after a backoff delay. In polling mode, it means that the last poll * request failed, and a new poll request will be made after the configured polling interval. */ INTERRUPTED, /** * Indicates that the data source has been permanently shut down. *

* This could be because it encountered an unrecoverable error (for instance, the LaunchDarkly service * rejected the SDK key; an invalid SDK key will never become valid), or because the SDK client was * explicitly shut down. */ OFF; } /** * An enumeration describing the general type of an error reported in {@link ErrorInfo}. * * @see ErrorInfo#getKind() */ public static enum ErrorKind { /** * An unexpected error, such as an uncaught exception, further described by {@link ErrorInfo#getMessage()}. */ UNKNOWN, /** * An I/O error such as a dropped connection. */ NETWORK_ERROR, /** * The LaunchDarkly service returned an HTTP response with an error status, available with * {@link ErrorInfo#getStatusCode()}. */ ERROR_RESPONSE, /** * The SDK received malformed data from the LaunchDarkly service. */ INVALID_DATA, /** * The data source itself is working, but when it tried to put an update into the data store, the data * store failed (so the SDK may not have the latest data). *

* Data source implementations do not need to report this kind of error; it will be automatically * reported by the SDK whenever one of the update methods of {@link DataSourceUpdates} throws an exception. */ STORE_ERROR } /** * A description of an error condition that the data source encountered. * * @see Status#getLastError() */ public static final class ErrorInfo { private final ErrorKind kind; private final int statusCode; private final String message; private final Instant time; /** * Constructs an instance. * * @param kind the general category of the error * @param statusCode an HTTP status or zero * @param message an error message if applicable, or null * @param time the error timestamp */ public ErrorInfo(ErrorKind kind, int statusCode, String message, Instant time) { this.kind = kind; this.statusCode = statusCode; this.message = message; this.time = time; } /** * Constructs an instance based on an exception. * * @param kind the general category of the error * @param t the exception * @return an ErrorInfo */ public static ErrorInfo fromException(ErrorKind kind, Throwable t) { return new ErrorInfo(kind, 0, t.toString(), Instant.now()); } /** * Constructs an instance based on an HTTP error status. * * @param statusCode the status code * @return an ErrorInfo */ public static ErrorInfo fromHttpError(int statusCode) { return new ErrorInfo(ErrorKind.ERROR_RESPONSE, statusCode, null, Instant.now()); } /** * Returns an enumerated value representing the general category of the error. * * @return the general category of the error */ public ErrorKind getKind() { return kind; } /** * Returns the HTTP status code if the error was {@link ErrorKind#ERROR_RESPONSE}, or zero otherwise. * * @return an HTTP status or zero */ public int getStatusCode() { return statusCode; } /** * Returns any additional human-readable information relevant to the error. The format of this message * is subject to change and should not be relied on programmatically. * * @return an error message if applicable, or null */ public String getMessage() { return message; } /** * Returns the date/time that the error occurred. * * @return the error timestamp */ public Instant getTime() { return time; } @Override public boolean equals(Object other) { if (other instanceof ErrorInfo) { ErrorInfo o = (ErrorInfo)other; return kind == o.kind && statusCode == o.statusCode && Objects.equals(message, o.message) && Objects.equals(time, o.time); } return false; } @Override public int hashCode() { return Objects.hash(kind, statusCode, message, time); } @Override public String toString() { StringBuilder s = new StringBuilder(); s.append(kind.toString()); if (statusCode > 0 || !Strings.isNullOrEmpty(message)) { s.append("("); if (statusCode > 0) { s.append(statusCode); } if (!Strings.isNullOrEmpty(message)) { if (statusCode > 0) { s.append(","); } s.append(message); } s.append(")"); } if (time != null) { s.append("@"); s.append(time.toString()); } return s.toString(); } } /** * Information about the data source's status and about the last status change. */ public static final class Status { private final State state; private final Instant stateSince; private final ErrorInfo lastError; /** * Constructs a new instance. * * @param state the basic state as an enumeration * @param stateSince timestamp of the last state transition * @param lastError a description of the last error, or null if no errors have occurred since startup */ public Status(State state, Instant stateSince, ErrorInfo lastError) { this.state = state; this.stateSince = stateSince; this.lastError = lastError; } /** * Returns an enumerated value representing the overall current state of the data source. * * @return the basic state */ public State getState() { return state; } /** * Returns the date/time that the value of {@link #getState()} most recently changed. *

* The meaning of this depends on the current state: *

    *
  • For {@link State#INITIALIZING}, it is the time that the SDK started initializing. *
  • For {@link State#VALID}, it is the time that the data source most recently entered a valid * state, after previously having been either {@link State#INITIALIZING} or {@link State#INTERRUPTED}. *
  • For {@link State#INTERRUPTED}, it is the time that the data source most recently entered an * error state, after previously having been {@link State#VALID}. *
  • For {@link State#OFF}, it is the time that the data source encountered an unrecoverable error * or that the SDK was explicitly shut down. *
* * @return the timestamp of the last state change */ public Instant getStateSince() { return stateSince; } /** * Returns information about the last error that the data source encountered, if any. *

* This property should be updated whenever the data source encounters a problem, even if it does * not cause {@link #getState()} to change. For instance, if a stream connection fails and the * state changes to {@link State#INTERRUPTED}, and then subsequent attempts to restart the * connection also fail, the state will remain {@link State#INTERRUPTED} but the error information * will be updated each time-- and the last error will still be reported in this property even if * the state later becomes {@link State#VALID}. * * @return a description of the last error, or null if no errors have occurred since startup */ public ErrorInfo getLastError() { return lastError; } @Override public boolean equals(Object other) { if (other instanceof Status) { Status o = (Status)other; return state == o.state && Objects.equals(stateSince, o.stateSince) && Objects.equals(lastError, o.lastError); } return false; } @Override public int hashCode() { return Objects.hash(state, stateSince, lastError); } @Override public String toString() { return "Status(" + state + "," + stateSince + "," + lastError + ")"; } } /** * Interface for receiving status change notifications. */ public static interface StatusListener { /** * Called when any property of the data source status has changed. * * @param newStatus the new status */ public void dataSourceStatusChanged(Status newStatus); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy