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

io.zeebe.test.broker.protocol.commandapi.CommandApiRule Maven / Gradle / Ivy

/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
 * one or more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * Licensed under the Zeebe Community License 1.0. You may not use this file
 * except in compliance with the Zeebe Community License 1.0.
 */
package io.zeebe.test.broker.protocol.commandapi;

import static io.zeebe.protocol.Protocol.START_PARTITION_ID;
import static io.zeebe.test.util.TestUtil.doRepeatedly;
import static io.zeebe.test.util.TestUtil.waitUntil;

import io.atomix.cluster.AtomixCluster;
import io.atomix.cluster.Member;
import io.zeebe.protocol.impl.encoding.BrokerInfo;
import io.zeebe.protocol.record.ValueType;
import io.zeebe.protocol.record.intent.JobBatchIntent;
import io.zeebe.protocol.record.intent.JobIntent;
import io.zeebe.test.broker.protocol.MsgPackHelper;
import io.zeebe.transport.ClientTransport;
import io.zeebe.transport.TransportFactory;
import io.zeebe.util.sched.ActorScheduler;
import io.zeebe.util.sched.clock.ControlledActorClock;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.agrona.collections.Int2ObjectHashMap;
import org.junit.rules.ExternalResource;

public final class CommandApiRule extends ExternalResource {

  private static final String DEFAULT_WORKER = "defaultWorker";

  private final int nodeId;
  private final Supplier atomixSupplier;
  private MsgPackHelper msgPackHelper;
  private int defaultPartitionId = -1;
  private final Int2ObjectHashMap testPartitionClients =
      new Int2ObjectHashMap<>();
  private final ControlledActorClock controlledActorClock = new ControlledActorClock();
  private ActorScheduler scheduler;

  public CommandApiRule(final Supplier atomixSupplier) {
    this.nodeId = 0;
    this.atomixSupplier = atomixSupplier;
  }

  @Override
  protected void before() {
    scheduler =
        ActorScheduler.newActorScheduler()
            .setCpuBoundActorThreadCount(1)
            .setActorClock(controlledActorClock)
            .build();
    scheduler.start();

    msgPackHelper = new MsgPackHelper();

    waitForTopology();
    final List partitionIds = doRepeatedly(this::getPartitionIds).until(p -> !p.isEmpty());
    defaultPartitionId = partitionIds.get(0);
  }

  @Override
  protected void after() {

    if (scheduler != null) {
      scheduler.stop();
    }
  }

  private void waitForTopology() {
    waitUntil(() -> getBrokerInfoStream().count() > 0);
  }

  /** targets the default partition by default */
  public ExecuteCommandRequestBuilder createCmdRequest() {
    return createCmdRequest(defaultPartitionId);
  }

  public ExecuteCommandRequestBuilder createCmdRequest(final int partition) {
    final var outputAdapter = createClientTransport();

    final var broker =
        getBrokerInfoStream().filter(brokerInfo -> brokerInfo.getNodeId() == nodeId).findFirst();

    if (broker.isPresent()) {
      final var brokerInfo = broker.get();

      return new ExecuteCommandRequestBuilder(
              outputAdapter, brokerInfo.getCommandApiAddress(), msgPackHelper)
          .partitionId(partition);
    }
    throw new IllegalStateException("Node " + nodeId + "not yet available");
  }

  private ClientTransport createClientTransport() {
    final var atomixCluster = fetchAtomix();
    return new TransportFactory(scheduler)
        .createClientTransport(atomixCluster.getMessagingService());
  }

  public PartitionTestClient partitionClient() {
    return partitionClient(defaultPartitionId);
  }

  public PartitionTestClient partitionClient(final int partitionId) {
    if (!testPartitionClients.containsKey(partitionId)) {
      testPartitionClients.put(partitionId, new PartitionTestClient(this, partitionId));
    }
    return testPartitionClients.get(partitionId);
  }

  public ExecuteCommandRequest activateJobs(
      final int partitionId,
      final String type,
      final long lockDuration,
      final int maxJobsToActivate) {
    // to make sure that job already exist
    partitionClient(partitionId)
        .receiveJobs()
        .withIntent(JobIntent.CREATED)
        .withType(type)
        .getFirst();

    return createCmdRequest(partitionId)
        .type(ValueType.JOB_BATCH, JobBatchIntent.ACTIVATE)
        .command()
        .put("type", type)
        .put("worker", DEFAULT_WORKER)
        .put("timeout", lockDuration)
        .put("maxJobsToActivate", maxJobsToActivate)
        .put("jobs", Collections.emptyList())
        .done()
        .send();
  }

  public ExecuteCommandRequest activateJobs(
      final int partitionId, final String type, final long lockDuration) {
    return activateJobs(partitionId, type, lockDuration, 10);
  }

  public List getPartitionIds() {
    return getBrokerInfoStream()
        .findFirst()
        .map(
            brokerInfo ->
                IntStream.range(
                        START_PARTITION_ID, START_PARTITION_ID + brokerInfo.getPartitionsCount())
                    .boxed()
                    .collect(Collectors.toList()))
        .orElse(Collections.emptyList());
  }

  private Stream getBrokerInfoStream() {
    final AtomixCluster atomixCluster = fetchAtomix();

    return atomixCluster.getMembershipService().getMembers().stream()
        .map(Member::properties)
        .map(BrokerInfo::fromProperties)
        .filter(Objects::nonNull);
  }

  private AtomixCluster fetchAtomix() {
    final AtomixCluster atomixCluster = atomixSupplier.get();
    Objects.requireNonNull(atomixCluster);
    return atomixCluster;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy