Client-side response routing for Spring

Overview

Riptide: A next generation HTTP client

Tidal wave

Stability: Active Build Status Coverage Status Code Quality Javadoc Release Maven Central License

Riptide noun, /ˈrɪp.taɪd/: strong flow of water away from the shore

Riptide is a library that implements client-side response routing. It tries to fill the gap between the HTTP protocol and Java. Riptide allows users to leverage the power of HTTP with its unique API.

  • Technology stack: Based on spring-web and uses the same foundation as Spring's RestTemplate.
  • Status: Actively maintained and used in production.
  • Riptide is unique in the way that it doesn't abstract HTTP away, but rather embrace it!

🚨 Upgrading from 2.x to 3.x? Please refer to the Migration Guide.

Example

Usage typically looks like this:

http.get("/repos/{org}/{repo}/contributors", "zalando", "riptide")
    .dispatch(series(),
        on(SUCCESSFUL).call(listOf(User.class), users -> 
            users.forEach(System.out::println)));

Feel free to compare this e.g. to Feign or Retrofit.

Features

Origin

Most modern clients try to adapt HTTP to a single-return paradigm as shown in the following example. Even though this may be perfectly suitable for most applications it takes away a lot of the power that comes with HTTP. It's not easy to support multiple different return values, i.e. distinct happy cases. Access to response headers or manual content negotiation are also harder to do.

@GET
@Path("/repos/{org}/{repo}/contributors")
List<User> getContributors(@PathParam String org, @PathParam String repo);

Riptide tries to counter this by providing a different approach to leverage the power of HTTP. Go checkout the concept document for more details.

Dependencies

  • Spring 4.1 or higher
    • ⚠️ Spring Boot integration requires Spring 5

Installation

Add the following dependency to your project:

<dependency>
    <groupId>org.zalando</groupId>
    <artifactId>riptide-core</artifactId>
    <version>${riptide.version}</version>
</dependency>

Additional modules/artifacts of Riptide always share the same version number.

Alternatively, you can import our bill of materials...

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.zalando</groupId>
      <artifactId>riptide-bom</artifactId>
      <version>${riptide.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

... which allows you to omit versions:

<dependencies>
  <dependency>
      <groupId>org.zalando</groupId>
      <artifactId>riptide-core</artifactId>
  </dependency>
  <dependency>
      <groupId>org.zalando</groupId>
      <artifactId>riptide-failsafe</artifactId>
  </dependency>
  <dependency>
      <groupId>org.zalando</groupId>
      <artifactId>riptide-faults</artifactId>
  </dependency>
</dependencies>

Configuration

Integration of your typical Spring Boot Application with Riptide, Logbook and Tracer can be greatly simplified by using the Riptide: Spring Boot Starter. Go check it out!

Http.builder()
    .executor(Executors.newCachedThreadPool())
    .requestFactory(new HttpComponentsClientHttpRequestFactory())
    .baseUrl("https://api.github.com")
    .converter(new MappingJackson2HttpMessageConverter())
    .converter(new Jaxb2RootElementHttpMessageConverter())
    .plugin(new OriginalStackTracePlugin())
    .build();

The following code is the bare minimum, since a request factory is required:

Http.builder()
    .executor(Executors.newCachedThreadPool())
    .requestFactory(new HttpComponentsClientHttpRequestFactory())
    .build();

This defaults to:

Thread Pool

All off the standard Executors.new*Pool() implementations only support the queue-first style, i.e. the pool scales up to the core pool size, then fills the queue and only then will scale up to the maximum pool size.

Riptide provides a ThreadPoolExecutors.builder() which also offers a scale-first style where thread pools scale up to the maximum pool size before they queue any tasks. That usually leads to higher throughput, lower latency on the expense of having to maintain more threads.

The following table shows which combination of properties are supported

Configuration Supported
Without queue, fixed size¹ ✔️
Without queue, elastic size² ✔️
Bounded queue, fixed size ✔️
Bounded queue, elastic size ✔️
Unbounded queue, fixed size ✔️
Unbounded queue, elastic size ³
Scale first, without queue, fixed size
Scale first, without queue, elastic size
Scale first, bounded queue, fixed size
Scale first, bounded queue, elastic size ✔️
Scale first, unbounded queue, fixed size
Scale first, unbounded queue, elastic size ✔️

¹ Core pool size = maximum pool size
² Core pool size < maximum pool size
³ Pool can't grow past core pool size due to unbounded queue
⁴ Scale first has no meaning without a queue
⁵ Fixed size pools are already scaled up
⁶ Elastic, but only between 0 and maximum pool size

Examples

  1. Without queue, elastic size

    ThreadPoolExecutors.builder()
        .withoutQueue()
        .elasticSize(5, 20)
        .keepAlive(1, MINUTES)
        .build(ze
  2. Bounded queue, fixed size

    ThreadPoolExecutors.builder()
        .boundedQueue(20)
        .fixedSize(20)
        .keepAlive(1, MINUTES)
        .build()
  3. Scale-first, unbounded queue, elastic size

    ThreadPoolExecutors.builder()
        .scaleFirst()
        .unboundedQueue()
        .elasticSize(20)   
        .keepAlive(1, MINUTES)
        .build()

You can read more about scale-first here:

In order to configure the thread pool correctly, please refer to How to set an ideal thread pool size.

Non-blocking IO

Riptide supports two different kinds of request factories:

ClientHttpRequestFactory

The following implementations offer blocking IO:

AsyncClientHttpRequestFactory

The following implementations offer non-blocking IO:

Non-blocking IO is asynchronous by nature. In order to provide asynchrony for blocking IO you need to register an executor. Not passing an executor will make all network communication synchronous, i.e. all futures returned by Riptide will already be completed.

Synchronous Asynchronous
Blocking IO ClientHttpRequestFactory Executor + ClientHttpRequestFactory
Non-blocking IO n/a AsyncClientHttpRequestFactory

Usage

Requests

A full-blown request may contain any of the following aspects: HTTP method, request URI, query parameters, headers and a body:

http.post("/sales-order")
    .queryParam("async", "false")
    .contentType(CART)
    .accept(SALES_ORDER)
    .header("Client-IP", "127.0.0.1")
    .body(cart)
    //...

Riptide supports the following HTTP methods: get, head, post, put, patch, delete, options and trace respectively. Query parameters can either be provided individually using queryParam(String, String) or multiple at once with queryParams(Multimap<String, String>).

The following operations are applied to URI Templates (get(String, Object...)) and URIs (get(URI)) respectively:

URI Template

URI

  • none, used as is
  • expected to be already encoded

Both

  • after respective transformation
  • resolved against Base URL (if present)
  • Query String (merged with existing)
  • Normalization

The URI Resolution table shows some examples how URIs are resolved against Base URLs, based on the chosen resolution strategy.

The Content-Type- and Accept-header have type-safe methods in addition to the generic support that is header(String, String) and headers(HttpHeaders).

Responses

Riptide is special in the way it handles responses. Rather than having a single return value, you need to register callbacks. Traditionally you would attach different callbacks for different response status codes, alternatively there are also built-in routing capabilities on status code families (called series in Spring) as well as on content types.

http.post("/sales-order")
    // ...
    .dispatch(series(),
        on(SUCCESSFUL).dispatch(contentType(),
            on(SALES_ORDER).call(SalesOrder.class, this::persist),
        on(CLIENT_ERROR).dispatch(status(),
            on(CONFLICT).call(this::retry),
            on(PRECONDITION_FAILED).call(this::readAgainAndRetry),
            anyStatus().call(problemHandling())),
        on(SERVER_ERROR).dispatch(status(),
            on(SERVICE_UNAVAILABLE).call(this::scheduleRetryLater))));

The callbacks can have the following signatures:

persist(SalesOrder)
retry(ClientHttpResponse)
scheduleRetryLater()

Futures

Riptide will return a CompletableFuture<ClientHttpResponse>. That means you can choose to chain transformations/callbacks or block on it.

If you need proper return values take a look at Riptide: Capture.

Exceptions

The only special custom exception you may get is UnexpectedResponseException, if and only if there was no matching condition and no wildcard condition either.

Plugins

Riptide comes with a way to register extensions in the form of plugins.

  • OriginalStackTracePlugin, preserves stack traces when executing requests asynchronously
  • AuthorizationPlugin, adds Authorization support
  • FailsafePlugin, adds retries, circuit breaker, backup requests and timeout support
  • MicrometerPlugin, adds metrics for request duration
  • TransientFaults, detects transient faults, e.g. network issues Whenever you encounter the need to perform some repetitive task on the futures returned by a remote call, you may consider implementing a custom Plugin for it.

Plugins are executed in phases:

Plugin phases

Please consult the Plugin documentation for details.

Testing

Riptide is built on the same foundation as Spring's RestTemplate and AsyncRestTemplate. That allows us, with a small trick, to use the same testing facilities, the MockRestServiceServer:

RestTemplate template = new RestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(template);
ClientHttpRequestFactory requestFactory = template.getRequestFactory();

Http.builder()
    .requestFactory(requestFactory)
    // continue configuration

We basically use an intermediate RestTemplate as a holder of the special ClientHttpRequestFactory that the MockRestServiceServer manages.

If you are using the Spring Boot Starter the test setup is provided by a convenient annotation @RiptideClientTest, see here.

Getting help

If you have questions, concerns, bug reports, etc., please file an issue in this repository's Issue Tracker.

Getting involved/Contributing

To contribute, simply make a pull request and add a brief description (1-2 sentences) of your addition or change. For more details check the contribution guidelines.

Credits and references

Comments
  • JAXB Implementation not found in Java 11 when using Failsafe with Retries & Spring Boot

    JAXB Implementation not found in Java 11 when using Failsafe with Retries & Spring Boot

    When using riptide-soap together with retries from riptide-failsafe in Java 11 on a packaged Spring Boot jar, the call fails with javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath. despite the correct JARs being in the classpath.

    Description

    In this specific combination:

    • riptide-soap
    • riptide-failsafe with retries enabled
    • Java 11
    • Packaged spring boot jar

    The call to create a JAXBContext with the aforementioned message. If retries are disabled, everything works as expected.

    The root cause of the issue seems like that Failsafe is using ForkJoinPool.commonPool() internally, resulting us in hitting https://github.com/spring-projects/spring-boot/issues/15737.

    Possible Fix

    I can think of two options:

    • Explicitly passing the current class' class loader to JAXBContext.newInstance(). This requires us to switch to creating JAXBContext with contextPath instead of the class to be bound.
    • Explicitly supplying an Executor to Failsafe that creates threads with the proper context class loader.

    I don't know which of these options is better, but I'd be happy to implement the change if there is some consensus.

    Steps to Reproduce

    I can provide a sample project if needed

    Your Environment

    • Version used: 3.0.0-RC.14
    Bug Help Wanted 
    opened by tobias-bahls 23
  • Should retries be restricted to idempotent requests?

    Should retries be restricted to idempotent requests?

    Detailed Description

    The failsafe plugin issues retries based on the exception but completely ignores the request method, i.e. even POST requests are retried right now.

    Context

    The backup request already filters for safe request methods. Maybe it's safer to do something similar for retries?

    Possible Implementation

    No concrete idea yet, but there is a chance of sharing some code between backup request and failsafe plugin, since all safe methods are inherently idempotent.

    Your Environment

    • Version used: 2.9.0-RC.3
    Feature Discussion 
    opened by whiskeysierra 17
  • BOM pulls in Spring BOM version

    BOM pulls in Spring BOM version

    I am not sure if the following is intended or not:

    riptide-bom inherits from riptide-parent, which pins a whole lot of additional dependencies other than riptide. Among these are the BOM for spring-boot-dependencies and spring-framework.

    When using Spring's dependency management plugin and gradle to import riptide's bom it'll override the project's Spring Boot version (and in turn all other dependencies defined by Spring):

    buildscript {
        dependencies {
            classpath "org.springframework.boot:spring-boot-gradle-plugin:2.2.2.RELEASE"
        }
    }
    
    ...
    
    dependencyManagement {
        imports {
            mavenBom "org.zalando:riptide-bom:3.0.0-RC.9"
        }
    }
    
    ...
    

    Now riptide-bom:3.0.0-RC.9 pulls in dependencies declared in riptide-parent, which in turn defines spring-boot-dependencies at version 2.2.1.RELEASE. This will then be the version used for the whole project. The same applies for all dependencies defined in the parent pom's <dependencyManagement> section.

    Expected Behavior

    Riptide-bom defines only riptide dependencies and nothing else.

    Actual Behavior

    Riptide-bom defines dependency versions other than riptide. Among these is spring-boot-dependencies BOM. This messes the full dependency tree up and is rather difficult to mitigate.

    Possible Fix

    1. Do not inherit from riptide-parent in riptide-bom.
    2. Split riptide-parent into two poms: one that defines metadata, e.g. riptide-build, and another that inherits from it and defines project-wide versions. Then inherit riptide-bom from riptide-build.
    3. Do not add <dependencyManagement> section in riptide-parent. (Somewhat defeats the purpose of it than :D)

    (The same issue applies to logbook-bom. I'm not going to create a ticket there for now.)

    Does that make sense? Maybe I am doing something stupid here?!

    Bug 
    opened by jrehwaldt 15
  • Added resolving beans by type

    Added resolving beans by type

    Description

    Inject tracer by bean reference instead of name

    Motivation and Context

    Currently tracer bean is injected by name. This leads to incompatibility issues with tracing-lightstep-spring-boot-starter > 0.1.5 Instead of relying on bean name for plugin registration, I've changed to loading by type.

    Types of changes

    • [ ] Bug fix (non-breaking change which fixes an issue)
    • [ ] New feature (non-breaking change which adds functionality)
    • [x] Breaking change (fix or feature that would cause existing functionality to change)

    Checklist:

    • [x] My change requires a change to the documentation.
    • [ ] I have updated the documentation accordingly.
    • [ ] I have added tests to cover my changes.
    opened by fatroom 14
  • Immutable Requester

    Immutable Requester

    Instead of modifying headers/query parameters and returning this, we may return a modified copy of the requester. That allows client to prepare requesters.

    Feature Discussion 
    opened by whiskeysierra 13
  • Riptide Client Testing

    Riptide Client Testing

    Introducing @RiptideClientTest - a new spring-boot meta-annotation to support testing riptide-spring-boot-starter generated Riptide Http clients.

    Currently testing is a bit tedious and repetitive. It can be shorter:

    @Component
    public class GatewayService {
    
        @Autowired
        @Qualifier("example")
        private Http http;
    
        void remoteCall() {
            http.get("/bar").dispatch(status(), on(OK).call(pass())).join();
        }
    }
    
    @RunWith(SpringRunner.class)
    @RiptideClientTest(GatewayService.class)
    public class RiptideTest {
    
        @Autowired
        private GatewayService client;
    
        @Autowired
        private MockRestServiceServer server;
    
        @Test
        public void shouldAutowireMockedHttp() throws Exception {
            server.expect(requestTo("https://example.com/bar")).andRespond(withSuccess());
            client.remoteCall()
            server.verify();
        }
    }
    

    @RiptideClientTest is based on @RestClientTest.

    Currently @RiptideClientTest and its facilities are tied to riptide-spring-boot-starter. In order to allow usage with riptide-core only, it may make sense to extract the components to a dedicated artifact. Instead of modifying the RiptideRegistar directly, one would need to make sure that properly named AsyncClientHttpRequestFactory are registered beforehand (expose Registry#generateBeanName?). Added benefit is that we wouldn't need any provided testing dependencies anymore. On the other hand I am not sure whether anybody wants to / needs to use this without the starter...

    opened by lukasniemeier-zalando 13
  • Unify syntax

    Unify syntax

    rest.xyz(GET, "/products/{id}", id)
            .accept(MediaTypes.PRODUCT, MediaTypes.PROBLEM)
            .header("Foo", "bar")
            .dispatch(series(),
                on(SUCCESSFUL).capture(Product.class),
                anySeries().call(fail()));
    
    rest.xyz(GET, "/products/{id}", id)
            .dispatch(series(),
                on(SUCCESSFUL).capture(Product.class),
                anySeries().call(fail()));
    
    • [x] find good name for initial method
    Feature 
    opened by whiskeysierra 13
  • Do retries/backups occupy three threads?

    Do retries/backups occupy three threads?

    1. IO thread (first request)
    2. Scheduled retry thread
    3. IO thread (second request)

    Depends on which thread is executing the callbacks supplied to a CompletableFuture.

    Discussion 
    opened by whiskeysierra 12
  • Verify that capturing lists works

    Verify that capturing lists works

    rest.get("/api").dispatch(series(),
            on(SUCCESSFUL).capture(Body.class, this::map))
            .to(listOf(String.class));
    
    private List<String> map(final Body body) {
        // ...
    }
    
    Bug 
    opened by whiskeysierra 12
  • using riptide-spring-boot-starter causes field error

    using riptide-spring-boot-starter causes field error

    tried to integrate riptide-spring-boot-starter:2.6.0-RC2 into a project. but running the application results in a bean creation error. adding converters for TimeSpan and Ratio didn't help. any idea what is missing? see code for converter and error log below.

    package org.zalando.riptide.spring;
    
    import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.stereotype.Component;
    
    @Component
    @ConfigurationPropertiesBinding
    public class TimeSpanConverter implements Converter<String, TimeSpan> {
    
        @Override
        public TimeSpan convert(final String source) {
            if (source == null) {
                return null;
            }
            return TimeSpan.valueOf(source);
        }
    
    }
    
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'restClientPostProcessor' defined in class path resource [org/zalando/riptide/spring/RiptideAutoConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult: 14 errors
    Field error in object 'riptide' on field 'oauth.schedulingPeriod': rejected value [10 seconds]; codes [typeMismatch.riptide.oauth.schedulingPeriod,typeMismatch.oauth.schedulingPeriod,typeMismatch.schedulingPeriod,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.oauth.schedulingPeriod,oauth.schedulingPeriod]; arguments []; default message [oauth.schedulingPeriod]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'oauth.schedulingPeriod'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'oauth.connectTimeout': rejected value [1 second]; codes [typeMismatch.riptide.oauth.connectTimeout,typeMismatch.oauth.connectTimeout,typeMismatch.connectTimeout,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.oauth.connectTimeout,oauth.connectTimeout]; arguments []; default message [oauth.connectTimeout]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'oauth.connectTimeout'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'oauth.socketTimeout': rejected value [1500 milliseconds]; codes [typeMismatch.riptide.oauth.socketTimeout,typeMismatch.oauth.socketTimeout,typeMismatch.socketTimeout,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.oauth.socketTimeout,oauth.socketTimeout]; arguments []; default message [oauth.socketTimeout]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'oauth.socketTimeout'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].connectTimeout': rejected value [1000 milliseconds]; codes [typeMismatch.riptide.clients[nakadi].connectTimeout,typeMismatch.riptide.clients.connectTimeout,typeMismatch.clients[nakadi].connectTimeout,typeMismatch.clients.connectTimeout,typeMismatch.connectTimeout,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].connectTimeout,clients[nakadi].connectTimeout]; arguments []; default message [clients[nakadi].connectTimeout]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].connectTimeout'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].socketTimeout': rejected value [12000 milliseconds]; codes [typeMismatch.riptide.clients[nakadi].socketTimeout,typeMismatch.riptide.clients.socketTimeout,typeMismatch.clients[nakadi].socketTimeout,typeMismatch.clients.socketTimeout,typeMismatch.socketTimeout,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].socketTimeout,clients[nakadi].socketTimeout]; arguments []; default message [clients[nakadi].socketTimeout]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].socketTimeout'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].connectionTimeToLive': rejected value [30 seconds]; codes [typeMismatch.riptide.clients[nakadi].connectionTimeToLive,typeMismatch.riptide.clients.connectionTimeToLive,typeMismatch.clients[nakadi].connectionTimeToLive,typeMismatch.clients.connectionTimeToLive,typeMismatch.connectionTimeToLive,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].connectionTimeToLive,clients[nakadi].connectionTimeToLive]; arguments []; default message [clients[nakadi].connectionTimeToLive]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].connectionTimeToLive'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].threadPool.keepAlive': rejected value [1 minute]; codes [typeMismatch.riptide.clients[nakadi].threadPool.keepAlive,typeMismatch.riptide.clients.threadPool.keepAlive,typeMismatch.clients[nakadi].threadPool.keepAlive,typeMismatch.clients.threadPool.keepAlive,typeMismatch.keepAlive,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].threadPool.keepAlive,clients[nakadi].threadPool.keepAlive]; arguments []; default message [clients[nakadi].threadPool.keepAlive]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].threadPool.keepAlive'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].retry.backoff.delay': rejected value [110 milliseconds]; codes [typeMismatch.riptide.clients[nakadi].retry.backoff.delay,typeMismatch.riptide.clients.retry.backoff.delay,typeMismatch.clients[nakadi].retry.backoff.delay,typeMismatch.clients.retry.backoff.delay,typeMismatch.delay,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].retry.backoff.delay,clients[nakadi].retry.backoff.delay]; arguments []; default message [clients[nakadi].retry.backoff.delay]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].retry.backoff.delay'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].retry.backoff.maxDelay': rejected value [200 milliseconds]; codes [typeMismatch.riptide.clients[nakadi].retry.backoff.maxDelay,typeMismatch.riptide.clients.retry.backoff.maxDelay,typeMismatch.clients[nakadi].retry.backoff.maxDelay,typeMismatch.clients.retry.backoff.maxDelay,typeMismatch.maxDelay,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].retry.backoff.maxDelay,clients[nakadi].retry.backoff.maxDelay]; arguments []; default message [clients[nakadi].retry.backoff.maxDelay]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].retry.backoff.maxDelay'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].retry.maxDuration': rejected value [20 seconds]; codes [typeMismatch.riptide.clients[nakadi].retry.maxDuration,typeMismatch.riptide.clients.retry.maxDuration,typeMismatch.clients[nakadi].retry.maxDuration,typeMismatch.clients.retry.maxDuration,typeMismatch.maxDuration,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].retry.maxDuration,clients[nakadi].retry.maxDuration]; arguments []; default message [clients[nakadi].retry.maxDuration]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].retry.maxDuration'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].retry.jitter': rejected value [100 milliseconds]; codes [typeMismatch.riptide.clients[nakadi].retry.jitter,typeMismatch.riptide.clients.retry.jitter,typeMismatch.clients[nakadi].retry.jitter,typeMismatch.clients.retry.jitter,typeMismatch.jitter,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].retry.jitter,clients[nakadi].retry.jitter]; arguments []; default message [clients[nakadi].retry.jitter]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].retry.jitter'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].circuitBreaker.failureThreshold': rejected value [5 out of 20]; codes [typeMismatch.riptide.clients[nakadi].circuitBreaker.failureThreshold,typeMismatch.riptide.clients.circuitBreaker.failureThreshold,typeMismatch.clients[nakadi].circuitBreaker.failureThreshold,typeMismatch.clients.circuitBreaker.failureThreshold,typeMismatch.failureThreshold,typeMismatch.org.zalando.riptide.spring.Ratio,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].circuitBreaker.failureThreshold,clients[nakadi].circuitBreaker.failureThreshold]; arguments []; default message [clients[nakadi].circuitBreaker.failureThreshold]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.Ratio' for property 'clients[nakadi].circuitBreaker.failureThreshold'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.Ratio]]
    Field error in object 'riptide' on field 'clients[nakadi].circuitBreaker.delay': rejected value [10 seconds]; codes [typeMismatch.riptide.clients[nakadi].circuitBreaker.delay,typeMismatch.riptide.clients.circuitBreaker.delay,typeMismatch.clients[nakadi].circuitBreaker.delay,typeMismatch.clients.circuitBreaker.delay,typeMismatch.delay,typeMismatch.org.zalando.riptide.spring.TimeSpan,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].circuitBreaker.delay,clients[nakadi].circuitBreaker.delay]; arguments []; default message [clients[nakadi].circuitBreaker.delay]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.TimeSpan' for property 'clients[nakadi].circuitBreaker.delay'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.TimeSpan]]
    Field error in object 'riptide' on field 'clients[nakadi].circuitBreaker.successThreshold': rejected value [3 out of 5]; codes [typeMismatch.riptide.clients[nakadi].circuitBreaker.successThreshold,typeMismatch.riptide.clients.circuitBreaker.successThreshold,typeMismatch.clients[nakadi].circuitBreaker.successThreshold,typeMismatch.clients.circuitBreaker.successThreshold,typeMismatch.successThreshold,typeMismatch.org.zalando.riptide.spring.Ratio,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [riptide.clients[nakadi].circuitBreaker.successThreshold,clients[nakadi].circuitBreaker.successThreshold]; arguments []; default message [clients[nakadi].circuitBreaker.successThreshold]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.zalando.riptide.spring.Ratio' for property 'clients[nakadi].circuitBreaker.successThreshold'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.zalando.riptide.spring.Ratio]]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
    	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686)
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:524)
    	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
    	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
    	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120)
    	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
    	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
    	... 24 more
    
    Bug 
    opened by MarkusSiemensmeyer 11
  • Failsafe is mandatory dependency since RC.6

    Failsafe is mandatory dependency since RC.6

    Description

    Seems we got again the problem with optionality of dependency. Build where only auto starter is included fails with NoClassDefFoundError

    Expected Behavior

    Default spring starter project with riptide-spring-boot-starter should pass the ContextLoad test

    Actual Behavior

    Test fails with

    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.zalando.riptide.autoconfigure.RiptidePostProcessor]: Factory method 'riptidePostProcessor' threw exception; nested exception is java.lang.NoClassDefFoundError: net/jodah/failsafe/Policy
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
    	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622)
    	... 42 more
    Caused by: java.lang.NoClassDefFoundError: net/jodah/failsafe/Policy
    	at org.zalando.riptide.autoconfigure.RiptideAutoConfiguration.riptidePostProcessor(RiptideAutoConfiguration.java:37)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    	... 43 more
    

    Possible Fix

    Check what constructor referenced unconditionally and got Policy as argument.

    Steps to Reproduce

    1. Create maven spring boot project via start.spring.io
    2. Add riptide-spring-boot-starter as dependency
    3. Run ./mvnw test

    Context

    Your Environment

    • Version used: RC.6-RC-8
    • Link to your project:
    Bug 
    opened by fatroom 10
  • Bump dependency-check-maven from 7.4.2 to 7.4.3

    Bump dependency-check-maven from 7.4.2 to 7.4.3

    Bumps dependency-check-maven from 7.4.2 to 7.4.3.

    Release notes

    Sourced from dependency-check-maven's releases.

    Version 7.4.3

    Fixed

    • Fixed NPE when analyzing version ranges in NPM (#5158 & #5190)
    • Resolved several FP (#5191)

    See the full listing of changes.

    Changelog

    Sourced from dependency-check-maven's changelog.

    Version 7.4.3 (2022-12-29)

    Fixed

    • Fixed NPE when analyzing version ranges in NPM (#5158 & #5190)
    • Resolved several FP (#5191)

    See the full listing of changes.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • Is there a plan to upgrade to Spring boot 3 & Spring 6?

    Is there a plan to upgrade to Spring boot 3 & Spring 6?

    Is there a plan to upgrade to Spring boot 3 & Spring 6

    Context

    A project of our uses Riptide and we wanted to upgrade the Spring version in it but can't as Riptide still uses the previous version and has few deprecated classes.

    Feature 
    opened by KeshavRathi 2
  • `http_client_requests_seconds` Prometheus metrics missing with `retry` enabled

    `http_client_requests_seconds` Prometheus metrics missing with `retry` enabled

    Looks like when retry is enabled for the Prometheus does not return the http_client_requests_seconds metric for the configured client

    Description

    The stack we are using is SpringBoot 2.6.8, Riptide 3.2.2 and Kotlin/target Java 17 (eclipse-temurin), GET operation on the client. With this configuration:

    factoring-service:
          base-url: https://xxx.zalan.do/
          tracing:
            tags:
              peer.service: factoring-service
          retry:
            enabled: true
            fixed-delay: 10 milliseconds
            max-retries: 1
    

    The prometheus actuator doesn't return the http_client_requests_seconds metric (for other clients without retry everything is fine). While disabling it, so with just:

    factoring-service:
          base-url: https://xxx.zalan.do/
          tracing:
            tags:
              peer.service: factoring-service
    

    Everything is fine.

    Expected Behavior

    http_client_requests_seconds is returned in any case, when the client operation is called.

    Actual Behavior

    http_client_requests_seconds is missing for the client configured with retry enabled, while it is returned for all the other clients

    Steps to Reproduce

    • Configure a project with the previous stack (defined at the beginning of the description)
    • Configure 2 clients, one with retry and another without
    • Verify, after calling the client operations, that only for one of them the metric http_client_requests_seconds is available.

    Context

    Upgrade SpringBoot/Riptide/JDK in the project

    Bug 
    opened by gianvitom 10
  • Bump failsafe from 2.4.3 to 2.4.4

    Bump failsafe from 2.4.3 to 2.4.4

    Bumps failsafe from 2.4.3 to 2.4.4.

    Changelog

    Sourced from failsafe's changelog.

    2.4.4

    Bug Fixes

    • Fixed #298 - Fallback.onFailedAttempt not being called correctly

    Improvements

    • Fixed #296 - Add Automatic-Module-Name entry to the generated manifest file

    API Changes

    • Added a generic result type R to ExecutionContext, Execution, AsyncExecution, and AsyncRunnable. This ensures that result types are unified across the API. It does mean that there are a few minor breaking changes to the API:
      • ContextualSupplier now has an additional result type parameter R. Normally this type is used as lambda parameters where the type is inferred, so most users should not be impacted. But any explicit generic declaration of this type will not compile until the new parameter is added.
      • PolicyExecutor, which is part of the SPI, now accepts an additional result type parameter R. This is only relevant for SPI users who are implementing their own Policies.
    • Changed FailsafeExecutor.getAsyncExecution to accept AsyncRunnable instead of AsyncSupplier. This is a breaking change for any getAsyncExecution calls, but the fix is to simply remove any return statement. The reason for this change is that the provided object does not need to return a result since the result will already be passed asynchronously to one of the AsyncExecution complete or retry methods.
    Commits
    • e992fce [maven-release-plugin] prepare release failsafe-2.4.4
    • 15d539c Fix Fallback.onFailedAttempt
    • d057983 Added Automatic-Module-Name entry to the generated manifest file (#296)
    • 34a38f6 Improve javadocs wrt FailurePolicy exception handling conditions
    • ad163c1 Create maven.yml
    • 2e37486 Be slightly more defensive when clearing the forkJoinPoolThread ref in Delega...
    • 19dfd28 Update push-javadoc for the separate website repo
    • 062b910 Update links for failsafe.dev domain
    • c446fae Changed FailsafeExecutor.getAsyncExecution to accept an AsyncRunnable
    • 9ae6438 Added result type params and unified result types
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • UnsupportedOperationException when using RestOperations + UriTemplate via RequestEntity

    UnsupportedOperationException when using RestOperations + UriTemplate via RequestEntity

    I get a UnsupportedOperationException when I use the RestOperations#exchange for the RestOperations provided by riptide in combination with a uri template that created via the request entity builders.

    Expected Behavior

    RestOperations#exchangeshould honor the uri template.

    Actual Behavior

    java.lang.UnsupportedOperationException: null
    	at org.springframework.http.RequestEntity.getUrl(RequestEntity.java:165) ~[spring-web-5.3.4.jar:5.3.4]
    	at org.zalando.riptide.compatibility.HttpOperations.exchange(HttpOperations.java:304) ~[riptide-compatibility-3.0.0-RC.16.jar:na]
    	at org.zalando.riptide.compatibility.HttpOperations.exchange(HttpOperations.java:294) ~[riptide-compatibility-3.0.0-RC.16.jar:na]
    

    Possible Fix

    Check for subtype UriTemplateRequestEntity before calling RequestEntity#getUrl()?

    Steps to Reproduce

    @Autowired
    private RestOperations rest;
    
    // …
    
            RequestEntity<EventTypeSpec> requestEntity = RequestEntity.put("/event-types/{}", eventTypeSpec.getName()).body(eventTypeSpec);
            rest.exchange(requestEntity, Map.class);
    

    Your Environment

    • spring-web: 5.3.4
    • riptide: 3.0.0-RC.16
    Bug 
    opened by i8r 0
  • Configurable Call Timeout

    Configurable Call Timeout

    I would like to be able to configure timeouts on a per request basis similar to what OkHttp calls Call Timeout (link).

    Detailed Description

    My current understanding is, that following request-related timeouts can be configured:

    • connections.connect-timeout for starting the TCP connection
    • connections.socket-timeout for data flow/ packets
    • timeouts.global wrapping everything (retries, queuing, backup requests, fallbacks, etc)

    From my perspective, what is missing is a way to define a time limit for a complete HTTP call (only), including resolving DNS, connecting, writing the request body, server processing, as well as reading the response body. OkHttp calls this Call Timeout.

    Context

    Currently it appears to me that there are only super low-level or super high-level timeout configuration possibilities, which makes it hard to configure SLO-based timeouts. As upstream services often define latency objectives on a per-request basis, I would also like to configure my request timeouts on a per request basis.

    To be more specific: I am neither interested in a particular connections.connect-timeout nor a specific connections.socket-timeout as long as the entire HTTP request finishes within a given time.

    Unfortunately the timeouts.global is too global on the other hand because it spans multiple request, which means that configuring something like this...

    clients:
        example:
          connections:
            connect-timeout: 25 milliseconds
            socket-timeout: 25 milliseconds
          retry:
            enabled: true
            max-retries: 2
          timeouts:
            enabled: true
            global: 150 milliseconds
    

    ... could still result in the global timeout kicking in before any retry happens.

    Possible Implementation

    • a per call timeout (maybe via a plugin)
    • policy composition autoconfig possibilities

    Your Environment

    • Version used: 3.0.0-RC.15
    • pretty standard Springboot app (2.3.7.RELEASE)
    Feature Help Wanted 
    opened by jbspeakr 7
Releases(3.2.2)
Owner
Zalando SE
The org page for Zalando, Europe's leading online fashion platform. Visit opensource.zalando.com for project stats.
Zalando SE
Google HTTP Client Library for Java

Google HTTP Client Library for Java Description Written by Google, the Google HTTP Client Library for Java is a flexible, efficient, and powerful Java

Google APIs 1.3k Jan 4, 2023
Unirest in Java: Simplified, lightweight HTTP client library.

Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

Kong 2.4k Jan 5, 2023
Square’s meticulous HTTP client for the JVM, Android, and GraalVM.

OkHttp See the project website for documentation and APIs. HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP

Square 43.4k Jan 5, 2023
http-kit is a minimalist, event-driven, high-performance Clojure HTTP server/client library with WebSocket and asynchronous support

HTTP Kit A simple, high-performance event-driven HTTP client+server for Clojure CHANGELOG | API | current Break Version: [http-kit "2.5.3"] ; Publish

HTTP Client/Server for Clojure 2.3k Dec 31, 2022
Unirest in Java: Simplified, lightweight HTTP client library.

Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

Kong 2.4k Jan 5, 2023
A high-level and lightweight HTTP client framework for Java. it makes sending HTTP requests in Java easier.

A high-level and lightweight HTTP client framework for Java. it makes sending HTTP requests in Java easier.

dromara 1.2k Jan 8, 2023
A small client useful for a variety of tasks ranging from raiding to duping.

CornClient A small utility mod for Minecraft useful for a variety of tasks ranging from raiding to duping. Support You can open an issue for help or f

Arilius Collection 115 Jan 4, 2022
skid client 1.12.2

Outrage best skid client Build / Contribute Download intellij idea Import gradle project Run genIntellijRuns Open runClient configuration, and add -Df

null 7 Dec 2, 2022
russian "paid" client leak

rip-nclient russian scam paid client with broken autodupe and zero protection whatsoever should be certified rat free™ client is fucking terrible rega

sn01 9 Aug 20, 2022
This client is still in development - Xena is a 1.12.2 Minecraft Utility Mod designed for Anarchy servers such as 2b2t/9b9t/5b5t etc, Devs are not responsible for improper usage.

Xena-client This client is still in development - Xena is a 1.12.2 Minecraft Utility Mod designed for Anarchy servers such as 2b2t/9b9t/5b5t etc, Devs

PreparedSystem_32 4 Oct 18, 2021
AltiriaSmsJavaClient, the official Java client of Altiria

¡Atención! Este proyecto aún se encuentra en desarrollo. Pronto se publicará la versión final para su uso. Altiria, cliente SMS Java Altiria SMS Java

Altiria 4 Dec 5, 2022
1.8 PVP Hacked Client

danny-125 Peroxide Lite This is not the full version of Peroxide, join our discord to get Peroxide+ Discord Join our discord: https://discord.gg/vJfuC

null 16 Nov 30, 2022
Konas Client de-obfuscated and manually remaped by Gopro336, Perry, and other based people

Konas-Deobf-Remap This project doesent really have a purpose anymore now that the real source code has leaked (this is a higher version tho) Deobfusca

null 52 Dec 13, 2022
Produtos-api-client - Biblioteca de consumo de uma API Rest básica de produtos disponibilizada em outro repositório.

produtos-api-client Biblioteca de consumo de uma API Rest básica de produtos disponibilizada no repositório: clique aqui para acessar Com essa bibliot

null 1 Jan 4, 2022
Koios Java Client Library is based on Koios Elastic Query Layer for Cardano Node by Cardano Community Guild Operators

Koios Java Client What is Koios? Koios Java Client Library is based on Koios Elastic Query Layer for Cardano Node by Cardano Community Guild Operators

Dudi Edri 13 Dec 4, 2022
Share the chat messages across Minecraft Servers via HTTP backend powered by Spring Boot, this is the backend part of the project.

InterconnectedChat-Backend Share the chat messages across Minecraft Servers via HTTP backend powered by Spring Boot, this is the backend part of the p

贺兰星辰 3 Oct 6, 2021
Simple Java library that can export all of your Spring endpoints into a Postman Collection

Postman Exporter for Spring This project is a simple Java library that can export all of your Spring endpoints into a Postman Collection json, which y

Lucas Sampaio Dias 30 Sep 6, 2022
EssentialClient is a client side mod originally forked from Carpet Client for 1.15.2 that implements new client side features

EssentialClient EssentialClient is a client side only mod originally forked from Carpet Client for 1.15.2 that implements new client side features. Th

null 62 Jan 3, 2023
The goal of this project is to play with Spring Cloud Stream Event Routing and CloudEvents

The goal of this project is to play with Spring Cloud Stream Event Routing and CloudEvents. For it, we will implement a producer and consumer of news & alert events.

Ivan Franchin 6 Oct 28, 2022