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

io.hyperfoil.core.handlers.JsonHandler Maven / Gradle / Ivy

There is a newer version: 0.27.1
Show newest version
package io.hyperfoil.core.handlers;

import java.util.function.Consumer;
import java.util.function.Function;

import org.kohsuke.MetaInfServices;

import io.hyperfoil.api.config.Name;
import io.hyperfoil.api.processor.Processor;
import io.hyperfoil.api.processor.RequestProcessorBuilder;
import io.hyperfoil.api.processor.Transformer;
import io.hyperfoil.core.builders.ServiceLoadedBuilderProvider;
import io.netty.buffer.ByteBuf;
import io.hyperfoil.api.session.Session;

public class JsonHandler extends JsonParser implements Processor, Session.ResourceKey {

   public JsonHandler(String query, boolean delete, Transformer replace, Processor processor) {
      super(query.trim(), delete, replace, processor);

   }

   @Override
   public void before(Session session) {
      processor.before(session);
      if (replace != null) {
         replace.before(session);
      }
   }

   @Override
   public void process(Session session, ByteBuf data, int offset, int length, boolean isLast) {
      Context ctx = session.getResource(this);
      ctx.parse(ctx.wrap(data, offset, length), session, isLast);
   }

   @Override
   public void after(Session session) {
      if (replace != null) {
         replace.after(session);
      }
      processor.after(session);
      Context ctx = session.getResource(this);
      ctx.reset();
   }

   @Override
   public String toString() {
      return "JsonHandler{" +
            "query='" + query + '\'' +
            ", recorder=" + processor +
            '}';
   }

   @Override
   public void reserve(Session session) {
      super.reserve(session);
      session.declareResource(this, new Context());
   }

   @Override
   protected void record(JsonParser.Context context, Session session, ByteStream data, int offset, int length, boolean isLastPart) {
      processor.process(session, ((ByteBufByteStream) data).buffer, offset, length, isLastPart);
   }

   public class Context extends JsonParser.Context {
      ByteBufByteStream actualStream;

      Context() {
         super(self -> new ByteBufByteStream(null, self::release));
         actualStream = new ByteBufByteStream(this::retain, null);
      }

      public ByteStream wrap(ByteBuf data, int offset, int length) {
         actualStream.buffer = data;
         actualStream.readerIndex = offset;
         return actualStream;
      }

      @Override
      protected void replaceConsumer(Void ignored, Session session, ByteStream data, int offset, int length, boolean lastFragment) {
         replace.transform(session, ((ByteBufByteStream) data).buffer, offset, length, lastFragment, replaceBuffer);
      }
   }

   /**
    * Parses JSON responses using simple queries.
    */
   @MetaInfServices(RequestProcessorBuilder.class)
   @Name("json")
   public static class Builder extends BaseBuilder implements RequestProcessorBuilder {
      @Override
      public JsonHandler build(boolean fragmented) {
         Processor processor = this.processor.build(fragmented);
         Transformer replace = this.replace == null ? null : this.replace.build(fragmented);
         if (unquote) {
            processor = new JsonUnquotingTransformer(processor);
            if (replace != null) {
               replace = new JsonUnquotingTransformer(replace);
            }
         }
         return new JsonHandler(query, delete, replace, processor);
      }

      /**
       * If neither `delete` or `replace` was set this processor will be called with the selected parts.
       * In the other case the processor will be called with chunks of full (modified) JSON.
       * 

* Note that the `processor.before()` and `processor.after()` methods are called only once for each request, * not for the individual filtered items. * * @return Builder. */ public ServiceLoadedBuilderProvider processor() { return new ServiceLoadedBuilderProvider<>(RequestProcessorBuilder.class, locator, this::processor); } } static class ByteBufByteStream implements ByteStream { private final Function retain; private final Consumer release; private ByteBuf buffer; private int readerIndex; ByteBufByteStream(Function retain, Consumer release) { this.retain = retain; this.release = release; } @Override public boolean isReadable() { return readerIndex < buffer.writerIndex(); } @Override public byte readByte() { assert isReadable(); return buffer.getByte(readerIndex++); } @Override public int getByte(int index) { return buffer.getByte(index); } @Override public int writerIndex() { return buffer.writerIndex(); } @Override public int readerIndex() { return readerIndex; } @Override public void release() { buffer.release(); buffer = null; readerIndex = -1; release.accept(this); } @Override public ByteStream retain() { buffer.retain(); return retain.apply(this); } @Override public void moveTo(ByteStream other) { ByteBufByteStream o = (ByteBufByteStream) other; assert o.buffer == null; o.buffer = buffer; o.readerIndex = readerIndex; buffer = null; readerIndex = -1; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy