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

io.atomix.rest.resources.DocumentTreeResource Maven / Gradle / Ivy

/*
 * Copyright 2017-present Open Networking Foundation
 *
 * Licensed 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 io.atomix.rest.resources;

import com.google.common.collect.Maps;
import io.atomix.core.tree.AsyncAtomicDocumentTree;
import io.atomix.core.tree.AtomicDocumentTreeConfig;
import io.atomix.core.tree.AtomicDocumentTreeType;
import io.atomix.core.tree.DocumentPath;
import io.atomix.core.tree.IllegalDocumentModificationException;
import io.atomix.core.tree.NoSuchDocumentPathException;
import io.atomix.rest.AtomixResource;
import io.atomix.utils.time.Versioned;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * Document tree resource.
 */
@AtomixResource
@Path("/atomic-document-tree")
public class DocumentTreeResource extends PrimitiveResource, AtomicDocumentTreeConfig> {
  private static final Logger LOGGER = LoggerFactory.getLogger(DocumentTreeResource.class);

  public DocumentTreeResource() {
    super(AtomicDocumentTreeType.instance());
  }

  /**
   * Returns a document path for the given path params.
   */
  private DocumentPath getDocumentPath(List params) {
    if (params.isEmpty()) {
      return DocumentPath.ROOT;
    } else {
      return DocumentPath.from(params.stream().map(PathSegment::getPath).collect(Collectors.toList()));
    }
  }

  @GET
  @Path("/{name}/{path: .*}")
  @Produces(MediaType.APPLICATION_JSON)
  public void get(
      @PathParam("name") String name,
      @PathParam("path") List path,
      @Suspended AsyncResponse response) {
    getPrimitive(name).thenCompose(tree -> tree.get(getDocumentPath(path))).whenComplete((result, error) -> {
      if (error == null) {
        response.resume(Response.ok(new VersionedResult(result)).build());
      } else {
        LOGGER.warn("{}", error);
        response.resume(Response.serverError().build());
      }
    });
  }

  @POST
  @Path("/{name}/{path: .*}")
  @Consumes(MediaType.TEXT_PLAIN)
  @Produces(MediaType.APPLICATION_JSON)
  public void create(
      @PathParam("name") String name,
      @PathParam("path") List path,
      String value,
      @Suspended AsyncResponse response) {
    getPrimitive(name).thenCompose(tree -> tree.createRecursive(getDocumentPath(path), value)).whenComplete((result, error) -> {
      if (error == null) {
        response.resume(Response.ok(result).build());
      } else {
        LOGGER.warn("{}", error);
        response.resume(Response.serverError().build());
      }
    });
  }

  @PUT
  @Path("/{name}/{path: .*}")
  @Consumes(MediaType.TEXT_PLAIN)
  @Produces(MediaType.APPLICATION_JSON)
  public void set(
      @PathParam("name") String name,
      @PathParam("path") List path,
      String value,
      @QueryParam("version") Long version,
      @Suspended AsyncResponse response) {
    CompletableFuture future;
    if (version != null) {
      future = getPrimitive(name).thenCompose(tree -> tree.replace(getDocumentPath(path), value, version));
    } else {
      future = getPrimitive(name).thenCompose(tree -> tree.set(getDocumentPath(path), value).thenApply(v -> Boolean.TRUE));
    }

    future.whenComplete((result, error) -> {
      if (error == null) {
        response.resume(Response.ok(result).build());
      } else {
        if (error.getCause() != null) {
          error = error.getCause();
        }
        if (error instanceof IllegalDocumentModificationException || error instanceof NoSuchDocumentPathException) {
          response.resume(Response.ok(false).build());
        } else {
          LOGGER.warn("{}", error);
          response.resume(Response.serverError().build());
        }
      }
    });
  }

  @GET
  @Path("/{name}/children")
  @Produces(MediaType.APPLICATION_JSON)
  public void getRootChildren(
      @PathParam("name") String name,
      @Suspended AsyncResponse response) {
    getPrimitive(name).thenCompose(tree -> tree.getChildren(tree.root())).whenComplete((result, error) -> {
      if (error == null) {
        response.resume(Response.ok(Maps.transformValues(result, VersionedResult::new)).build());
      } else {
        LOGGER.warn("{}", error);
        response.resume(Response.serverError().build());
      }
    });
  }

  @GET
  @Path("/{name}/children/{path: .*}")
  @Produces(MediaType.APPLICATION_JSON)
  public void getChildren(
      @PathParam("name") String name,
      @PathParam("path") List path,
      @Suspended AsyncResponse response) {
    getPrimitive(name).thenCompose(tree -> tree.getChildren(getDocumentPath(path))).whenComplete((result, error) -> {
      if (error == null) {
        response.resume(Response.ok(Maps.transformValues(result, VersionedResult::new)).build());
      } else {
        LOGGER.warn("{}", error);
        response.resume(Response.serverError().build());
      }
    });
  }

  @DELETE
  @Path("/{name}/{path: .*}")
  @Produces(MediaType.APPLICATION_JSON)
  public void removeNode(
      @PathParam("name") String name,
      @PathParam("path") List path,
      @Suspended AsyncResponse response) {
    getPrimitive(name).thenCompose(tree -> tree.remove(getDocumentPath(path))).whenComplete((result, error) -> {
      if (error == null) {
        response.resume(Response.ok(new VersionedResult(result)).build());
      } else {
        LOGGER.warn("{}", error);
        response.resume(Response.serverError().build());
      }
    });
  }

  /**
   * Versioned JSON result.
   */
  static class VersionedResult {
    private final Versioned value;

    VersionedResult(Versioned value) {
      this.value = value;
    }

    public String getValue() {
      return value.value();
    }

    public long getVersion() {
      return value.version();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy