// Module included in the following assemblies:
//
// * operators/operator_sdk/java/osdk-java-tutorial.adoc

:_mod-docs-content-type: PROCEDURE
[id="osdk-java-implement-controller_{context}"]
= Implementing the controller

After creating a new API and controller, you can implement the controller logic.

.Procedure

. Append the following dependency to the `pom.xml` file:
+
[source,xml]
----
    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.2</version>
    </dependency>
----

. For this example, replace the generated controller file `MemcachedReconciler.java` with following example implementation:
+
.Example `MemcachedReconciler.java`
[%collapsible]
====
[source,java]
----
package com.example;

import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
import io.fabric8.kubernetes.api.model.LabelSelectorBuilder;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.OwnerReferenceBuilder;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodSpecBuilder;
import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.DeploymentSpecBuilder;
import org.apache.commons.collections.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MemcachedReconciler implements Reconciler<Memcached> {
  private final KubernetesClient client;

  public MemcachedReconciler(KubernetesClient client) {
    this.client = client;
  }

  // TODO Fill in the rest of the reconciler

  @Override
  public UpdateControl<Memcached> reconcile(
      Memcached resource, Context context) {
      // TODO: fill in logic
      Deployment deployment = client.apps()
              .deployments()
              .inNamespace(resource.getMetadata().getNamespace())
              .withName(resource.getMetadata().getName())
              .get();

      if (deployment == null) {
          Deployment newDeployment = createMemcachedDeployment(resource);
          client.apps().deployments().create(newDeployment);
          return UpdateControl.noUpdate();
      }

      int currentReplicas = deployment.getSpec().getReplicas();
      int requiredReplicas = resource.getSpec().getSize();

      if (currentReplicas != requiredReplicas) {
          deployment.getSpec().setReplicas(requiredReplicas);
          client.apps().deployments().createOrReplace(deployment);
          return UpdateControl.noUpdate();
      }

      List<Pod> pods = client.pods()
          .inNamespace(resource.getMetadata().getNamespace())
          .withLabels(labelsForMemcached(resource))
          .list()
          .getItems();

      List<String> podNames =
          pods.stream().map(p -> p.getMetadata().getName()).collect(Collectors.toList());


      if (resource.getStatus() == null
               || !CollectionUtils.isEqualCollection(podNames, resource.getStatus().getNodes())) {
           if (resource.getStatus() == null) resource.setStatus(new MemcachedStatus());
           resource.getStatus().setNodes(podNames);
           return UpdateControl.updateResource(resource);
      }

      return UpdateControl.noUpdate();
  }

  private Map<String, String> labelsForMemcached(Memcached m) {
    Map<String, String> labels = new HashMap<>();
    labels.put("app", "memcached");
    labels.put("memcached_cr", m.getMetadata().getName());
    return labels;
  }

  private Deployment createMemcachedDeployment(Memcached m) {
      Deployment deployment = new DeploymentBuilder()
          .withMetadata(
              new ObjectMetaBuilder()
                  .withName(m.getMetadata().getName())
                  .withNamespace(m.getMetadata().getNamespace())
                  .build())
          .withSpec(
              new DeploymentSpecBuilder()
                  .withReplicas(m.getSpec().getSize())
                  .withSelector(
                      new LabelSelectorBuilder().withMatchLabels(labelsForMemcached(m)).build())
                  .withTemplate(
                      new PodTemplateSpecBuilder()
                          .withMetadata(
                              new ObjectMetaBuilder().withLabels(labelsForMemcached(m)).build())
                          .withSpec(
                              new PodSpecBuilder()
                                  .withContainers(
                                      new ContainerBuilder()
                                          .withImage("memcached:1.4.36-alpine")
                                          .withName("memcached")
                                          .withCommand("memcached", "-m=64", "-o", "modern", "-v")
                                          .withPorts(
                                              new ContainerPortBuilder()
                                                  .withContainerPort(11211)
                                                  .withName("memcached")
                                                  .build())
                                          .build())
                                  .build())
                          .build())
                  .build())
          .build();
    deployment.addOwnerReference(m);
    return deployment;
  }
}
----
====
+
The example controller runs the following reconciliation logic for each `Memcached` custom resource (CR):
+
--
* Creates a Memcached deployment if it does not exist.
* Ensures that the deployment size matches the size specified by the `Memcached` CR spec.
* Updates the `Memcached` CR status with the names of the `memcached` pods.
--
