public class GeneratorConfigException extends GeneratorException {
  /**
   * generated by eclipse
   */
  private static final long serialVersionUID = -2894157024668803650L;

  public GeneratorConfigException(String message, Throwable cause) {
    super(message, cause);
  }

  public GeneratorConfigException(String message) {
    super(message);
  }

  public GeneratorConfigException(Throwable cause) {
    super(cause);
  }

} public class GeneratorException extends SleetException {
  /**
   * generated by eclipse
   */
  private static final long serialVersionUID = -2408099842630238442L;

  public GeneratorException(String message, Throwable cause) {
    super(message, cause);
  }

  public GeneratorException(String message) {
    super(message);
  }

  public GeneratorException(Throwable cause) {
    super(cause);
  }

} public class GeneratorSessionException extends GeneratorException {
  /**
   * generated by eclipse
   */

  private static final long serialVersionUID = -5663201042060915360L;

  public GeneratorSessionException(String message, Throwable cause) {
    super(message, cause);
  }

  public GeneratorSessionException(String message) {
    super(message);
  }

  public GeneratorSessionException(Throwable cause) {
    super(cause);
  }

} public interface IdGenerator<T extends IdType<?, ?>> {
  public void beginIdSession(Properties config) throws SleetException;

  public void checkSessionValidity() throws SleetException;

  public void endIdSession() throws SleetException;

  public IdType<?, ?> getId(List<IdState<?, ?>> states) throws SleetException;
} public class IdOverflowException extends GeneratorException {
  /**
   * generated by eclipse
   */
  private static final long serialVersionUID = 6754925706659214152L;

  public IdOverflowException(String message, Throwable cause) {
    super(message, cause);
  }

  public IdOverflowException(String message) {
    super(message);
  }

  public IdOverflowException(Throwable cause) {
    super(cause);
  }

} public class IdOverrunException extends GeneratorException {
  /**
   * generated by eclipse
   */
  private static final long serialVersionUID = 6754925706659214152L;

  public IdOverrunException(String message, Throwable cause) {
    super(message, cause);
  }

  public IdOverrunException(String message) {
    super(message);
  }

  public IdOverrunException(Throwable cause) {
    super(cause);
  }

} public class FixedLongIdGenerator implements IdGenerator<LongIdType> {
  public static final String FIXED_LONG_VALUE_KEY = "fixed.long.value";
  public static final String FIXED_BITS_IN_ID_KEY = "fixed.bits.in.value";

  private LongId value = null;

  @Override
  public void beginIdSession(Properties config) throws SleetException {
    if (this.value != null) {
      throw new GeneratorSessionException("Session was already started.  Stop session by calling endIdSession() then start session by calling beginIdSession()");
    }
    String valueStr = config.getProperty(FIXED_LONG_VALUE_KEY);
    if (valueStr == null) {
      throw new GeneratorConfigException("Missing value for fixed long id generation, must be specified in configuration properties key \"" + FIXED_LONG_VALUE_KEY + "\".");
    }
    long value = -1;
    try {
      value = Long.valueOf(valueStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse long value from value \"" + valueStr + "\".  The value for configuration properties key \"" + FIXED_LONG_VALUE_KEY + "\" must be a long.");
    }

    String bitsStr = config.getProperty(FIXED_BITS_IN_ID_KEY);
    if (bitsStr == null) {
      throw new GeneratorConfigException("Missing number of bits for the fixed value, must be specified in configuration properties key \"" + FIXED_BITS_IN_ID_KEY + "\".");
    }
    int bits = -1;
    try {
      bits = Integer.valueOf(bitsStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse number of bits from value \"" + bitsStr + "\".  The value for configuration properties key \"" + FIXED_BITS_IN_ID_KEY + "\" must be a long.");
    }

    if ((1L << bits) - 1L < value) {
      throw new GeneratorConfigException("Specified value of " + value + " exceeds the capacity of the number of bits specified (" + bits + ").");
    }

    this.value = new LongId(value, null, bits);
  }

  @Override
  public void checkSessionValidity() throws SleetException {
    validateSessionStarted();
  }

  @Override
  public void endIdSession() throws SleetException {
    validateSessionStarted();
    this.value = null;
  }

  @Override
  public LongIdType getId(List<IdState<?, ?>> states) throws SleetException {
    validateSessionStarted();
    return this.value;
  }

  private void validateSessionStarted() throws GeneratorSessionException {
    if (this.value == null) {
      throw new GeneratorSessionException("Session was not started.  Start session by calling beginIdSession()");
    }
  }
} public class ZooKeeperReservedInstanceIdGenerator implements IdGenerator<LongIdType> {
  private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperReservedInstanceIdGenerator.class);

  public static final String ZK_SERVER_KEY = "zk.reserved.instance.zk.server";
  public static final String ZK_PATH_KEY = "zk.reserved.instance.zk.path";
  public static final String ZK_TIMEOUT_KEY = "zk.reserved.instance.zk.timeout";
  public static final String BITS_IN_INSTANCE_ID_KEY = "zk.reserved.instance.bits.in.instance.value";
  public static final String MILLISECONDS_TO_WAIT_FOR_ID = "zk.reserved.instance.milliseconds.to.wait.for.startup";

  private int bitsInInstanceValue = -1;
  private int maxInstanceValue = -1;
  ZooKeeper zk = null;
  private String path = null;
  private InstanceIdManagerForwardReservationZooKeeper instMgr = null;
  private LongId id = null;
  private Properties originalConfig = null;

  public ZooKeeperReservedInstanceIdGenerator() {
  }

  ZooKeeper getZk() {
    return zk;
  }

  @Override
  public void beginIdSession(Properties config) throws SleetException {
    originalConfig = new Properties(config);
    if (this.zk != null) {
      throw new GeneratorSessionException("Session was already started.  Stop session by calling endIdSession() then start session by calling beginIdSession()");
    }

    String zkStr = config.getProperty(ZK_SERVER_KEY);
    if (zkStr == null) {
      throw new GeneratorConfigException("Missing ZooKeeper server string, must be specified in configuration properties key \"" + ZK_SERVER_KEY + "\".");
    }

    String zkPathStr = config.getProperty(ZK_PATH_KEY);
    if (zkPathStr == null) {
      throw new GeneratorConfigException("Missing ZooKeeper path string, must be specified in configuration properties key \"" + ZK_PATH_KEY + "\".");
    }

    this.path = zkPathStr;

    String zkTimeoutStr = config.getProperty(ZK_TIMEOUT_KEY);
    if (zkTimeoutStr == null) {
      throw new GeneratorConfigException("Missing ZooKeeper timeout, must be specified in configuration properties key \"" + ZK_TIMEOUT_KEY + "\".");
    }
    int zkTimeout = -1;
    try {
      zkTimeout = Integer.valueOf(zkTimeoutStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse ZooKeeper timeout from value \"" + zkTimeoutStr + "\".  The value must be an integer.");
    }

    try {
      this.zk = new ZooKeeperClient(zkStr, zkTimeout, new Watcher() {
        @Override
        public void process(WatchedEvent event) {

        }
      });
    } catch (IOException e) {
      throw new SleetException(e);
    }

    String bitsStr = config.getProperty(BITS_IN_INSTANCE_ID_KEY);
    if (bitsStr == null) {
      throw new GeneratorConfigException("Missing number of bits, must be specified in configuration properties key \"" + BITS_IN_INSTANCE_ID_KEY + "\".");
    }
    int bits = -1;
    try {
      bits = Integer.valueOf(bitsStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse number of bits from value \"" + bitsStr + "\".  The value must be an integer.");
    }
    this.bitsInInstanceValue = bits;
    this.maxInstanceValue = (1 << this.bitsInInstanceValue) - 1;

    String msTimeoutStr = config.getProperty(MILLISECONDS_TO_WAIT_FOR_ID);
    if (msTimeoutStr == null) {
      throw new GeneratorConfigException("Missing number of milliseconds to wait on startup, must be specified in configuration properties key \"" + MILLISECONDS_TO_WAIT_FOR_ID + "\".");
    }
    int msTimeout = -1;
    try {
      msTimeout = Integer.valueOf(msTimeoutStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse number of milliseconds to wait on startup from value \"" + msTimeoutStr + "\".  The value must be an integer.");
    }

    try {
      this.instMgr = new InstanceIdManagerForwardReservationZooKeeper(this.zk, this.path, this.maxInstanceValue + 1, config);
      long underlyingid = this.instMgr.tryToGetId(msTimeout);
      if (underlyingid == -1) {
        throw new ReservedInstanceTimeoutException("Unable to allocate an id instance within the timeout allotted (" + msTimeout + ")");
      } else {
        this.id = new LongId(underlyingid, null, this.bitsInInstanceValue);
      }
    } catch (IOException e) {
      throw new SleetException(e);
    }
  }

  @Override
  public void checkSessionValidity() throws SleetException {
    checkSessionValidity(true);
  }

  public void checkSessionValidity(boolean allowValidityStateCaching) throws SleetException {
    validateSessionStarted();
    try {
      if (!this.instMgr.sessionValid(allowValidityStateCaching)) {
        // throw new GeneratorSessionException("Underlying " +
        // this.instMgr.getClass().getName() +
        // " implementation lost session validity.");
        if (LOG.isDebugEnabled()) {
          LOG.debug("Terminating existing invalid session and beginning a new one.");
        }
        terminateIdSession();
        beginIdSession(originalConfig);
      }
    } catch (IOException e) {
      throw new SleetException(e);
    }
  }

  private void terminateIdSession() throws SleetException {
    try {
      this.instMgr.releaseId(this.id.getId().intValue());
    } catch (IOException e) {
      throw new SleetException(e);
    }
    try {
      this.zk.close();
    } catch (InterruptedException e) {
      throw new SleetException(e);
    }
    this.bitsInInstanceValue = -1;
    this.maxInstanceValue = -1;
    this.zk = null;
    this.path = null;
    this.instMgr = null;
    this.id = null;
  }

  @Override
  public void endIdSession() throws SleetException {
    checkSessionValidity(false);
    terminateIdSession();
  }

  @Override
  public LongIdType getId(List<IdState<?, ?>> states) throws SleetException {
    checkSessionValidity(true);
    try {
      this.id = new LongId(this.instMgr.getCurrentId(), null, this.bitsInInstanceValue);
    } catch (IOException e) {
      throw new SleetException(e);
    }
    return this.id;
  }

  private void validateSessionStarted() throws GeneratorSessionException {
    if (this.id == null) {
      throw new GeneratorSessionException("Session was not started.  Start session by calling beginIdSession()");
    }
  }

} public class SequenceIdOverflowError extends BaseIdError {

  public SequenceIdOverflowError(String message) {
    super(message);
  }
} public class TimeDependentSequenceIdGenerator implements IdGenerator<LongIdType> {
  public static final String BITS_IN_SEQUENCE_KEY = "sequence.bits.in.sequence.value";

  private int bits = -1;
  private long maxSequenceValue = -1;
  private long lastTimeValue = -1;

  private long sequenceValue = 0;

  @Override
  public void beginIdSession(Properties config) throws SleetException {
    if (this.maxSequenceValue != -1) {
      throw new GeneratorSessionException("Session was already started.  Stop session by calling endIdSession() then start session by calling beginIdSession()");
    }
    String bitsStr = config.getProperty(BITS_IN_SEQUENCE_KEY);
    if (bitsStr == null) {
      throw new GeneratorConfigException("Missing number of bits for the sequence value, must be specified in configuration properties key \""
          + BITS_IN_SEQUENCE_KEY + "\".");
    }
    int bits = -1;
    try {
      bits = Integer.valueOf(bitsStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse number of bits from value \"" + bitsStr + "\".  The value for configuration properties key \""
          + BITS_IN_SEQUENCE_KEY + "\" must be a long.");
    }

    this.bits = bits;

    this.maxSequenceValue = (1L << bits) - 1L;
  }

  @Override
  public void checkSessionValidity() throws SleetException {
    validateSessionStarted();
  }

  @Override
  public void endIdSession() throws SleetException {
    validateSessionStarted();
    this.maxSequenceValue = -1;
  }

  @Override
  public LongIdType getId(List<IdState<?, ?>> states) throws SleetException {
    validateSessionStarted();
    TimeIdType timeIdType = null;
    for (IdState<?, ?> state : states) {
      if (state.getId() instanceof TimeIdType) {
        if (timeIdType == null) {
          timeIdType = (TimeIdType) state.getId();
        } else {
          throw new TimeDependencyException(this.getClass().getName() + " depends on there being a single preceeding TimeIdType id, but found at least two.");
        }
      }
    }
    if (timeIdType == null) {
      throw new TimeDependencyException(this.getClass().getName() + " depends on there being a single preceeding TimeIdType id, but found none.");
    }

    long currentTimeValue = timeIdType.getId();
    long returnValue = -1;
    if (currentTimeValue < this.lastTimeValue) {
      return new LongId(-1, new TimeIdReverseSkewError(this.getClass().getName()
          + " depends on the preceeding id generator which generated the TimeIdType to guard against the TimeIdType values decreasing over time"), this.bits);
    } else if (currentTimeValue == this.lastTimeValue) {
      if (this.sequenceValue > this.maxSequenceValue || this.sequenceValue < 0) {
        return new LongId(-1, new SequenceIdOverflowError(this.getClass().getName()
            + " overflowed the maximum sequence value when allocating a sequence id for time value \"" + currentTimeValue + "\"."), this.bits);
      } else {
        returnValue = this.sequenceValue;
        this.sequenceValue++;
      }
    } else {
      this.sequenceValue = 0;
      returnValue = this.sequenceValue;
      this.sequenceValue++;
    }
    this.lastTimeValue = currentTimeValue;
    return new LongId(returnValue, null, this.bits);
  }

  private void validateSessionStarted() throws GeneratorSessionException {
    if (this.maxSequenceValue == -1) {
      throw new GeneratorSessionException("Session was not started.  Start session by calling beginIdSession()");
    }
  }

} public class TimeIdGenerator implements IdGenerator<TimeIdType> {

  public static final String EPOCH_KEY = "time.epoch.in.java.system.time.millis";
  public static final String GRANULARITY_IN_MS_KEY = "time.granularity.milliseconds";
  public static final String BITS_IN_TIME_VALUE_KEY = "time.bits.in.time.value";
  public static final String MAX_WAIT_AFTER_BACKWARD_CLOCK_SKEW_KEY = "time.max.wait.milliseconds.after.backward.clock.skew";

  private TimeCalculator timeCalc;
  private long maxWaitAfterBackwardClockSkew = -1;
  private long lastTimeValue = -1;

  @Override
  public void endIdSession() throws SleetException {
    validateSessionStarted();
    this.timeCalc = null;
  }

  public void configure(Properties config) throws SleetException {
    String epochStr = config.getProperty(EPOCH_KEY);
    if (epochStr == null) {
      throw new GeneratorConfigException("Missing epoch for time calculation, must be specified in configuration properties key \"" + EPOCH_KEY + "\".");
    }
    long epoch = -1;
    try {
      epoch = Long.valueOf(epochStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse epoch time from value \"" + epochStr + "\".  The value for configuration properties key \"" + EPOCH_KEY + "\" must be a long.");
    }
    if (epoch < 0) {
      throw new GeneratorConfigException("Epoch specified (" + epoch + ") is negative.");
    }
    if (epoch > System.currentTimeMillis()) {
      throw new GeneratorConfigException("Epoch specified (" + epoch + ") is in the future.");
    }
    String granularityStr = config.getProperty(GRANULARITY_IN_MS_KEY);
    if (granularityStr == null) {
      throw new GeneratorConfigException("Missing granularity (in milliseconds), must be specified in configuration properties key \"" + GRANULARITY_IN_MS_KEY + "\".");
    }
    long granularityMs = -1;
    try {
      granularityMs = Long.valueOf(granularityStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse granularity (in milliseconds) from value \"" + granularityStr + "\".  The value for configuration properties key \"" + GRANULARITY_IN_MS_KEY
          + "\" must be a long.");
    }
    if (granularityMs < 1) {
      throw new GeneratorConfigException("Granularity < 1ms is not supported.");
    }
    String bitsStr = config.getProperty(BITS_IN_TIME_VALUE_KEY);
    if (bitsStr == null) {
      throw new GeneratorConfigException("Missing number of bits, must be specified in configuration properties key \"" + BITS_IN_TIME_VALUE_KEY + "\".");
    }
    int bits = -1;
    try {
      bits = Integer.valueOf(bitsStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse number of bits from value \"" + bitsStr + "\".  The value must be an integer.");
    }

    String maxWaitAfterBackwardClockSkewStr = config.getProperty(MAX_WAIT_AFTER_BACKWARD_CLOCK_SKEW_KEY);
    if (maxWaitAfterBackwardClockSkewStr == null) {
      throw new GeneratorConfigException("Missing maximum wait (in ms) after backward time skew, must be specified in configuration properties key \"" + MAX_WAIT_AFTER_BACKWARD_CLOCK_SKEW_KEY + "\".");
    }
    long maxWaitAfterBackwardClockSkew = -1;
    try {
      maxWaitAfterBackwardClockSkew = Long.valueOf(maxWaitAfterBackwardClockSkewStr);
    } catch (NumberFormatException e) {
      throw new GeneratorConfigException("Failed to parse maximum wait (in ms) after backward time skew from value \"" + maxWaitAfterBackwardClockSkewStr + "\".  The value must be a long.");
    }

    this.maxWaitAfterBackwardClockSkew = maxWaitAfterBackwardClockSkew;

    this.timeCalc = new TimeCalculator(epoch, granularityMs, TimeUnit.MILLISECONDS, bits);
  }
  
  @Override
  public void beginIdSession(Properties config) throws SleetException {
    if (this.timeCalc != null) {
      throw new GeneratorSessionException("Session was already started.  Stop session by calling endIdSession() then start session by calling beginIdSession()");
    }
    this.configure(config);
  }
  
  public TimeCalculator getTimeCalculator() {
    return this.timeCalc;
  }

  @Override
  public TimeIdType getId(List<IdState<?, ?>> states) throws SleetException {
    validateSessionStarted();
    /**
     * MCM validate the clock didn't move backward enough to reduce the time
     * value since last time
     */
    long timeValue = this.timeCalc.timeValue();
    if (timeValue < this.lastTimeValue) {
      long deltaMs = this.timeCalc.millisSinceJavaEpochUTC(this.lastTimeValue) - this.timeCalc.millisSinceJavaEpochUTC(timeValue);
      if (deltaMs <= this.maxWaitAfterBackwardClockSkew) {
        /**
         * MCM if config allows us to sleep, just sleep, otherwise we error
         * below for the process above to handle
         */
        while (timeValue < this.lastTimeValue) {
          try {
            Thread.sleep(deltaMs);
          } catch (InterruptedException e) {
            // ignore
          }
          timeValue = this.timeCalc.timeValue();
          deltaMs = this.timeCalc.millisSinceJavaEpochUTC(this.lastTimeValue) - this.timeCalc.millisSinceJavaEpochUTC(timeValue);
        }
        this.lastTimeValue = timeValue;
        return new TimeId(timeValue, null, this.timeCalc.getBitsInTimeValue());
      } else {
        return new TimeId(timeValue, new TimeIdReverseSkewError("Time skewed backward by " + deltaMs + "ms.", deltaMs), this.timeCalc.getBitsInTimeValue());
      }
    } else {
      this.lastTimeValue = timeValue;
      return new TimeId(timeValue, null, this.timeCalc.getBitsInTimeValue());
    }
  }

  @Override
  public void checkSessionValidity() throws SleetException {
    validateSessionStarted();
  }

  private void validateSessionStarted() throws GeneratorSessionException {
    if (this.timeCalc == null) {
      throw new GeneratorSessionException("Session was not started.  Start session by calling beginIdSession()");
    }
  }

  public void sleepUntilNextTimeValue(long lastValue) throws TimeCalculationException {
    LockSupport.parkUntil(this.timeCalc.millisSinceJavaEpochUTC(lastValue + 1));
  }
} public class TimeIdReverseSkewError extends BaseIdError implements TimeIdError {
  private final long deltaMs;

  public TimeIdReverseSkewError(String message) {
    super(message);
    this.deltaMs = -1;
  }

  public TimeIdReverseSkewError(String message, long deltaMs) {
    super(message);
    this.deltaMs = deltaMs;
  }

  public long getDeltaMs() {
    return deltaMs;
  }
} public interface IdType<T, E extends IdError> {
  public T getId();

  public E getError();

  public int getNumBitsInId();
} public class LongId implements LongIdType {
  private final long value;
  private final int numBits;
  private final IdError error;

  public LongId(long value, IdError error, int numBits) {
    this.value = value;
    this.error = error;
    this.numBits = numBits;
  }

  @Override
  public Long getId() {
    return this.value;
  }

  @Override
  public IdError getError() {
    return this.error;
  }

  @Override
  public int getNumBitsInId() {
    return this.numBits;
  }

} public interface LongIdType extends IdType<Long, IdError> {

} public interface TimeIdType extends IdType<Long, TimeIdError> {

} public class IdState<T, E extends IdError> {
  private Class<? extends IdGenerator<? extends IdType<T, E>>> generatorClass;
  private IdType<T, E> id;

  public IdState(Class<? extends IdGenerator<? extends IdType<T, E>>> generatorClass, IdType<T, E> id) {
    this.generatorClass = generatorClass;
    this.id = id;
  }

  public Class<? extends IdGenerator<? extends IdType<T, E>>> getGeneratorClass() {
    return generatorClass;
  }

  public IdType<T, E> getId() {
    return id;
  }
}
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import sleet.generators.GeneratorConfigException;
import sleet.generators.GeneratorException;
import sleet.generators.GeneratorSessionException;
import sleet.generators.IdGenerator;
import sleet.generators.IdOverflowException;
import sleet.generators.IdOverrunException;
import sleet.generators.fixed.FixedLongIdGenerator;
import sleet.generators.reservedinstance.ZooKeeperReservedInstanceIdGenerator;
import sleet.generators.time.SequenceIdOverflowError;
import sleet.generators.time.TimeDependentSequenceIdGenerator;
import sleet.generators.time.TimeIdGenerator;
import sleet.generators.time.TimeIdReverseSkewError;
import sleet.id.IdType;
import sleet.id.LongId;
import sleet.id.LongIdType;
import sleet.id.TimeIdType;
import sleet.state.IdState;
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 sleet;



public class SleetIdGenerator implements IdGenerator<LongIdType> {
  public static final String WAIT_ON_SEQUENCE_OVERRUN_KEY = "sleet.wait.on.sequence.overrun";

  final List<IdGenerator<? extends IdType<?, ?>>> subgens = new ArrayList<IdGenerator<? extends IdType<?, ?>>>(4);

  private boolean waitOnSeqOverrun;

  private Object lock = new Object();

  public SleetIdGenerator() {
  }

  @Override
  public void beginIdSession(Properties config) throws SleetException {
    if (!this.subgens.isEmpty()) {
      throw new GeneratorSessionException("Session was already started.  Stop session by calling endIdSession() then start session by calling beginIdSession()");
    }
    TimeIdGenerator timeGen = new TimeIdGenerator();
    timeGen.beginIdSession(config);
    this.subgens.add(timeGen);

    FixedLongIdGenerator fixedGen = new FixedLongIdGenerator();
    fixedGen.beginIdSession(config);
    this.subgens.add(fixedGen);

    ZooKeeperReservedInstanceIdGenerator instanceGen = new ZooKeeperReservedInstanceIdGenerator();
    instanceGen.beginIdSession(config);
    this.subgens.add(instanceGen);

    TimeDependentSequenceIdGenerator seqGen = new TimeDependentSequenceIdGenerator();
    seqGen.beginIdSession(config);
    this.subgens.add(seqGen);

    String waitOnSeqOverrunStr = config.getProperty(WAIT_ON_SEQUENCE_OVERRUN_KEY);
    if (waitOnSeqOverrunStr == null) {
      throw new GeneratorConfigException("Missing flag for whether to wait on sequence overrun during id generation, must be specified in configuration properties key \""
          + WAIT_ON_SEQUENCE_OVERRUN_KEY + "\".");
    }
    this.waitOnSeqOverrun = Boolean.parseBoolean(waitOnSeqOverrunStr);
  }

  @Override
  public void checkSessionValidity() throws SleetException {
    validateSessionStarted();
    for (IdGenerator<?> subgen : this.subgens) {
      subgen.checkSessionValidity();
    }
  }

  private void validateSessionStarted() throws GeneratorSessionException {
    if (this.subgens.isEmpty()) {
      throw new GeneratorSessionException("Session was not started.  Start session by calling beginIdSession()");
    }
  }

  @Override
  public void endIdSession() throws SleetException {
    validateSessionStarted();
    for (IdGenerator<?> subgen : this.subgens) {
      subgen.endIdSession();
    }
    this.subgens.clear();
  }

  public LongIdType getId() throws SleetException {
    return getId(null);
  }

  @Override
  public LongIdType getId(List<IdState<?, ?>> states) throws SleetException {
    validateSessionStarted();
    synchronized (lock) {
      List<IdState<?, ?>> substates = new ArrayList<IdState<?, ?>>(4);
      long numBits = 0;
      for (IdGenerator<? extends IdType<?, ?>> subgen : this.subgens) {
        IdType<?, ?> subid = subgen.getId(substates);
        if (subid.getError() != null) {
          if (subid.getError() instanceof SequenceIdOverflowError) {
            if (waitOnSeqOverrun) {
              Long lastTimeValue = null;
              for (int i = 0; i < substates.size() && lastTimeValue == null; i++) {
                IdState<?, ?> substate = substates.get(i);
                if (substate.getId() instanceof TimeIdType) {
                  lastTimeValue = ((TimeIdType) substate.getId()).getId();
                }
              }
              if (lastTimeValue == null) {
                throw new GeneratorException("Time dependent sequence overflowed, but unable to find time id in existing id states upon which to base a sleep.");
              }
              /**
               * Sleep until next time value and re-run the id generation
               * process
               */
              ((TimeIdGenerator) this.subgens.get(0)).sleepUntilNextTimeValue(lastTimeValue);
              return getId(states);
            } else {
              throw new IdOverrunException("Time dependent sequence overflowed and Sleet is currently configured not to wait until new time value to proceed.");
            }
          } else if (subid.getError() instanceof TimeIdReverseSkewError) {