Git Product home page Git Product logo

resteasy-microprofile's Introduction

RESTEasy

Github CI

RESTEasy is a JBoss.org project aimed at providing productivity frameworks for developing client and server RESTful applications and services in Java. It is mainly a Jakarta RESTful Web Services implementation but you'll find some other experimental code in the repository.

The project page can be found at https://resteasy.dev.

Jakarta RESTful Web Services

RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It is a portable implementation of the Jakarta RESTful Web Services specification. The Jakarta RESTful Web Services provides a Java API for RESTful Web Services over the HTTP protocol. Please note that the specification is now under the Jakarta EE Project. You can read the entire specification at Jakarta RESTful Web Services.

Getting started with RESTEasy

Documentation

To read the documentation you can read it online.

Issues

Issues are kept in JIRA.

Build

Currently, RESTEasy requires JDK 11+.

If you want to build the project without running the tests, you need to pull down a clone of the RESTEasy repository and run:

$ ./mvnw clean install -DskipTests=true

If you want to build the project with testings run, you may need to specify a profile to use, and may need to configure the WildFly version you want to run the tests with. Here is an example:

$ SERVER_VERSION=27.0.0.Final ./mvnw clean -fae -Dserver.version=$SERVER_VERSION install

Contribute

You are most welcome to contribute to RESTEasy!

Read the Contribution guidelines

resteasy-microprofile's People

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

resteasy-microprofile's Issues

Provide a SPI for the InjectorFactory

In Quarkus, we end up having a copy of the following class just to override the InjectorFactory (with QuarkusInjectorFactory):
https://github.com/resteasy/resteasy-microprofile/blob/main/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientBuilderImpl.java#L127

I wonder if we could expose a SPI for that so that Quarkus is not forced to have the RESTEasy CDI dependency.

Note that it's going to be a pain to migrate automatically to Jakarta but... at least, it will be done and will be more future proof.

Removing the unnecessary warning when default exception mapper is disabled

Discussed in #46

Originally posted by moghaddam June 2, 2022
When the default exception mapper in MicroProfile Rest Client is disabled by setting the microprofile.rest.client.disable.default.mapper=true, a warning appears in the log for every single calls to RestClientBuilderImpl.build(). The log is added in resteasy/resteasy#2850 and you can see that there is also a comment by reviewer about reducing the log level for this message to debug, but it got merged anyway.

I was wondering if it should really be considered as warning ๐Ÿค”

We have recently upgraded to WildFly 26 and there are tons of such warnings in the log while it's an expected behavior in our case. I think others could have had a similar issue too.

Using restclient builder, exceptions are not mapped when calling subresources methods

Im using restclient builder to access jax-rs subresrources. However, when an error http response is received, instead of calling a registered ResponseExceptionMapper, a ExceptionMapping.HandlerException is thrown with the message 'Handled internally'. To get the correct runtime exception thrown, i have to surround my call in try/catch and call the mapping manually:

        try {
            return subResourceController.subresourceMethod(subresourceMethodParam);
        } catch (ExceptionMapping.HandlerException e) {
            // Mapping not performed on subresources :/
            try {
                Method subresourceMethod = SubResourceController.class.getMethod("subresourceMethod", SubresourceMethodParam.class);
                e.mapException(subresourceMethod); 
            } catch (RuntimeException e2) {
                throw e2; // rethrows the mapped exception
            } catch (Exception e3) {
                throw new AssertionError("Should not reach this", e3); // error during mapping
            }
            throw new AssertionError("Should not reach this", e); // exception not mapped
        }

With this construct, my ResponseExceptionMapper is called as expected, and I can access the desired fields (response status, body, ...)

Im using resteasy-client 6.2.4.Final and microprofile-rest-client 2.1.4.Final.

The exception is created there: https://github.com/resteasy/resteasy-microprofile/blob/main/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/ExceptionMapping.java#L50

The stacktrace leading to it is:

org.jboss.resteasy.microprofile.client.ExceptionMapping$HandlerException: Handled Internally
	at org.jboss.resteasy.microprofile.client.ExceptionMapping.filter(ExceptionMapping.java:113)
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.filterResponse(ClientInvocation.java:667)
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:428)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:134)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:103)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:61)

For a call on a normal resource (not a subresource), the HandlerException is created, from the same call stack; but later mapped and rethrown as a WebApplicationException:

at be.fiscalteam.nitro.it.client.util.ClientExceptionMapper.toThrowable(ClientExceptionMapper.java:17)
	at be.fiscalteam.nitro.it.client.util.ClientExceptionMapper.toThrowable(ClientExceptionMapper.java:9)
	at org.jboss.resteasy.microprofile.client.ExceptionMapping$HandlerException.mapException(ExceptionMapping.java:60)
	at org.jboss.resteasy.microprofile.client.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:147)

This is the be.fiscalteam.nitro.it.client.util.ClientExceptionMapper. registered using RestClientBuilder.newBuilder().register(new ClientExceptionMapper()):

public class ClientExceptionMapper implements ResponseExceptionMapper<WebApplicationException> {
    @Override
    public boolean handles(int status, MultivaluedMap<String, Object> headers) {
        return ResponseExceptionMapper.super.handles(status, headers);
    }

    @Override
    public WebApplicationException toThrowable(Response response) {
        return new WebApplicationException(response);
    }
}

The subresources I use are defined as simple Jax-rs interfaces, like for the normal resources but lacking a @path annotation on the class.
Examples:

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface WsNitroRuleActionsSubResourceController  {

    @POST
    @Path("")
    WsNitroRuleAction createNitroRuleAction(@NotNull WsNitroRuleAction wsNitroRuleAction);

    @GET
    @Path("/list")
    List<WsNitroRuleAction> listNitroRuleActions();
}

@Path("nitroRule")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface WsNitroRuleController  {

    @POST
    @Path("")
    WsNitroRule createNitroRule(@NotNull WsNitroRule wsNitroRule);

    @GET
    @Path("/{id: [0-9]+}")
    WsNitroRule getNitroRule(@PathParam("id") Long id);

    @Path("/{id: [0-9]+}/actions")
    WsNitroRuleActionsSubResourceController getActionsSubresource(@PathParam("id") Long id);

}

Upgrade Arquillian and WildFly Arquillian

Upgrade Arquillian to 1.8.0.Final and WildFly Arquillian to 5.1.0.beta2. The latter is required as some utilities were removed from org.jboss.resteasy:resteasy-arquillian-utils.

Upgrading these in a patch release is okay as they are only used for testing purposes.

Error occurred while reading SSEsjava.lang.IllegalStateException: RESTEASY004087: No suitable message body reader for class : jakarta.ws.rs.core.GenericType

thrown in SSEPublisher#pump() when REST resource method return type is not Publisher<String>.
In my case, the method signature is

@GET
  // the following give media type text/event-stream;element-type="application/json"
  @Produces("text/event-stream")
  @org.jboss.resteasy.annotations.SseElementType(MediaType.APPLICATION_JSON)

  // the following gives media type application/x-stream-general;element-type="application/json"
//  @org.jboss.resteasy.annotations.Stream
  Publisher<SseEventDto> getEvents();

SSEPublisher calls event.readData((Class) typeArgument) which tries to lookup a message bodyx reader for type MediaType.TEXT_PLAIN_TYPE.
SSEPublisher should call event.readData((Class) typeArgument, MediaType.APPLICATION_JSON) in this case to get the proper reader.

Here is how resteasy-rxjava2 handle this: e.readData(clazz, ((InboundSseEventImpl) e).getMediaType())

Split the feature pack

The galleon-feature-pack should be split into separate modules like WildFly's ee-feature-pack. The galleon-feature-pack should only have a pom.xml and the wildfly-feature-pack-build.xml. The modules and any tasks should be split out.

Migrate to RESTEasy 6.2.x and Jakarta REST 3.1

With MicroProfile 6.0 some specs are migrating to Jakarta EE 10 variants. The MicroProfile REST Client so far is not one of these. However, we should migrate the implementation itself for better alignment with RESTEasy.

Enable testing with the Security Manager enabled

There are likely failing tests when the security manager is enabled. There should be an easy way to test, e.g. pass -Dsecurity.manager, with the security manager enabled. WildFly and RESTEasy already do this. It should be enabled in a CI job too.

RestClientBuilder doesn't allow AutoCloseable extension of client interface with a type level URI template

The RestClientBuilder throws a RestClientDefinitionException when building from a client interface that extends AutoCloseable and has a URI template on the type level. The Exceptions says Rest client failed: org.eclipse.microprofile.rest.client.RestClientDefinitionException: Parameters and variables don't match on interface on interface Foo::close.

The specification says that the client interface is allowed to extend AutoCloseable. The implementation doesn't allow the unresolved URI template of the close method.

I'm having an issue with the way the `http.nonProxyHosts` Sytem Property is evaluated by `org.jboss.resteasy.microprofile.clientRestClientBuilderImpl`. I have thought of filing an Issue but I think this might be viewed as a question of design so let's discuss it.

I'm having an issue with the way the http.nonProxyHosts Sytem Property is evaluated by org.jboss.resteasy.microprofile.clientRestClientBuilderImpl. I have thought of filing an Issue but I think this might be viewed as a question of design so let's discuss it.

Scenario:
I need to access Service my-pod.someserver.somecluster.mycompany.com
For some reason there are Proxy settings -Dhttp.proxyHost=proxy.mycompany.com -Dhttp.proxyPort=8080 -Dhttp.nonProxyHosts=*.mycompany.com|127.*|localhost

Problem:
Given the Implementation of RestClientBuilderImpl.getProxyHostsAsRegex() my Service call will be directed via the Proxy.
Why is this? Because said method converts the Pattern *.mycompany.com to a regex [A-Za-z0-9-]*\\.mycompany\\.com. This regex does not match my-pod.someserver.somecluster.mycompany.com. It will match no address containing a . that is not explicitely written. And of course it will not match 127.0.0.1. This behaviour also is different from what's stated on the Oracle Web Site.
The only solution is for me - or whoever is in control of the JVM arguments - to add *.someserver.somecluster.mycompany.com to the http.nonProxyHosts Property. Assuming there are a number of different testing environments this can amount to an awful lot of *.someotherserver.productioncluster.mycompany.com-stuff to configure - that's what wildcards were introduced for in the first place, I assume.

So wouldn't it make sense to change the implementation of the Pattern Matching around RestClientBuilderImpl.getProxyHostsAsRegex() and RestClientBuilderImpl.build() to be more useful?
A minimal change to make the implementation fit my needs might be to simply expand [A-Za-z0-9-]* to [A-Za-z0-9-\\.]*
But - frankly: the Pattern .*\\.mycompany\\.com would do the job perfectly well.

Originally posted by @arne-reinhardt in #142

Add a test for multipart/form-data

With the upgrade to Jakarta REST 3.1, there is an EntityPart type which can be used to send multipart/form-data. While RESTEasy itself does test this, we should also ensure it works correctly with MicroProfile REST Clients.

Remove the dependency to resteasy-cdi

The MP REST Client implementation has a dependency on org.jboss.resteasy:resteasy-cdi. This is done to support CDI injection for RESTEasy. We could copy most of the resources in and likely simplify them. The one catch will be EJB stateless and singleton beans.

It may make the most sense for the RESTEasy MicroProfile REST Client implementation to have it's own CDI extension. This will need to be looked at though.

Remove the provided scope from the BOM dependencies

The user BOM includes some Jakarta specifications that are marked as <scope>provided</scope>. For WildFly this is fine, but not all containers will explicitly include these dependencies. Remove the scope so they can be inherited.

Fix the BOM's to be real BOM's

The current BOM's are more BOM's for the project and not users. We should have a BOM for the project and a consumable one for users which only includes org.jboss.resteasy.microprofile based dependencies in the dependencyManagement.

Rest Client java.util.regex.PatternSyntaxException: Dangling meta character '*' near index 0 when setting http.nonProxyHosts

The issue seems to be here https://github.com/resteasy/Resteasy/blob/8e64b0650e0066ac1b73299affac3facd182a988/resteasy-client-microprofile-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientBuilderImpl.java#L256. I don't think it can be assumed that the host is a valid regex pattern. The [networking properties documentation|https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html] talks about how {{*}} is used for wildcards.

This was originally found in Quarkus quarkusio/quarkus#19240. There are more details there.

Add missing community files

There are some standard community files that are missing from the project. We should add the following files:

  • SECURITY.md
  • CODEOWNERS
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.adoc
  • dco.txt

We also need to ensure the POM has the correct license, SCM and issue management settings.

Migrate to Java 11

SmallRye Jakarta Implementations moved to Java 11 smallrye/smallrye-parent#349. Since this project, even though it's optional, requires SmallRye Config we need to migrate to Java 11. This seems like a reasonable move regardless as Jakarta EE 10 now requires Java 11.

MP RestClient - register ResponseExceptionMapper with a Feature

I want to implement a feature to add authorization to a rest client.
This feature should handle following steps

  • request an access token
  • cache access token
  • add authorization header to requests
  • reset access token when http 401 occurs
  • throw a specific exception if a request fails with http 401

The problem is that the ResponseExceptionMapper is not registered and executed.

ServiceREST.java

@RegisterRestClient(baseUri = "http://localhost:4567/api")
@RegisterProvider(AuthorizationFeature.class)
@Retry(retryOn = RetryException.class, maxRetries = 1)
public interface ServiceREST {

    @GET
    @Path("test")
    String test() throws RetryException;
}

AuthorizationFeature.java

@ApplicationScoped
public class AuthorizationFeature implements Feature {

    private String currentToken;
    private final Object currentTokenLock = new Object();

    @Inject
    @RestClient
    AuthorizationREST authorizationREST;

    @Override
    public boolean configure(FeatureContext featureContext) {

        featureContext.register(clientRequestFilter((clientRequestContext) -> {
            try {
                final String token = getOrRequestToken();
                clientRequestContext.setProperty("TOKEN", token);
                clientRequestContext.getHeaders().add("Authorization", "Bearer " + token);
            }
            catch (WebApplicationException e) {
                final Response r = Response.fromResponse(e.getResponse())
                        .header("REQUEST_TOKEN_ERROR", "Unable to request Token")
                        .build();

                clientRequestContext.abortWith(r);
            }
        }));

        featureContext.register(clientResponseFilter((clientRequestContext, clientResponseContext) -> {
            if (clientResponseContext.getHeaderString("REQUEST_TOKEN_ERROR") != null) {
                clientResponseContext.getHeaders().add("ALLOW_RETRY", "FALSE");
            }
            else if (clientResponseContext.getStatus() == 401) {
                resetInvalidToken(clientRequestContext.getProperty("TOKEN").toString());
                clientResponseContext.getHeaders().add("ALLOW_RETRY", "TRUE");
            }
        }));

        featureContext.register(new ResponseExceptionMapper<RetryException>() {
            @Override
            public RetryException toThrowable(Response response) {
                return new RetryException();
            }

            @Override
            public boolean handles(int status, MultivaluedMap<String, Object> headers) {
                if (status == 401 && headers.get("ALLOW_RETRY") != null) {
                    return headers.get("ALLOW_RETRY").contains("TRUE");
                }
                return false;
            }
        });

        return true;
    }

    protected ClientRequestFilter clientRequestFilter(ClientRequestFilter clientRequestFilter) {
        return clientRequestFilter;
    }

    protected ClientResponseFilter clientResponseFilter(ClientResponseFilter clientResponseFilter) {
        return clientResponseFilter;
    }

    public String getOrRequestToken() {
        String token = currentToken;
        if (token == null) {
            synchronized (currentTokenLock) {
                if (currentToken == null) {
                    currentToken = authorizationREST.token();
                }
                token = currentToken;
                if (token == null) {
                    throw new RuntimeException("Token is null!");
                }
            }
        }
        return token;
    }

    public void resetInvalidToken(String invalidToken) {
        if (invalidToken.equals(currentToken)) {
            synchronized (currentTokenLock) {
                if (invalidToken.equals(currentToken)) {
                    currentToken = null;
                }
            }
        }
    }
}

With MP Fault Tolerance it's possible to retry the request if the access token is invalid.

Introduce a WildFly Channel for RESTEasy MicroProfile

We currently provide feature packs for WildFly to provision upgraded components. We can do this now better with WildFly Channels and the user would just need to add a new channel.

This was introduced into RESTEasy with RESTEASY-3432. We should do the same here as well.

For the 2.1 branch we should keep the modules as users may be using that. For the main (3.0) branch, we should remove the feature packs. The feature packs themselves can be kept, but modules should be removed.

RestClientBuilderImpl is very verbose when CDI is not enabled

The RestClientBuilderImpl logs a warning every time the BeanManager is looked up and CDI is not activated. In some scenarios this could be in an SE environment with no container or just be in a managed bean. We should better examine our environment or log this as a debug message instead of a warning.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.