Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to use application-fury in resteasy #26

Merged
merged 4 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-common-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.fury</groupId>
<artifactId>quarkus-fury</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.DotName;

import io.quarkiverse.fury.ClassicFurySerializer;
import io.quarkiverse.fury.ClassicFurySerializerProducer;
import io.quarkiverse.fury.FuryBuildTimeConfig;
import io.quarkiverse.fury.FuryProducer;
import io.quarkiverse.fury.FuryRecorder;
import io.quarkiverse.fury.FurySerialization;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem;

class FuryProcessor {

Expand Down Expand Up @@ -71,4 +76,17 @@ public FuryBuildItem setup(
FuryBuildTimeConfig config, BeanContainerBuildItem beanContainer, FuryRecorder recorder) {
return new FuryBuildItem(recorder.createFury(config, beanContainer.getValue()));
}

@BuildStep
public void registerResteasyClassicIntegration(Capabilities capabilities,
BuildProducer<ResteasyJaxrsProviderBuildItem> resteasyJaxrsProviderBuildItemBuildProducer,
BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
if (capabilities.isPresent(Capability.RESTEASY)) {
additionalBeans.produce(AdditionalBeanBuildItem.builder().setUnremovable()
.addBeanClasses(ClassicFurySerializerProducer.class)
.build());
resteasyJaxrsProviderBuildItemBuildProducer
.produce(new ResteasyJaxrsProviderBuildItem(ClassicFurySerializer.class.getName()));
}
}
}
2 changes: 1 addition & 1 deletion integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.fury</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;

import org.apache.fury.BaseFury;
import org.apache.fury.Fury;
Expand Down Expand Up @@ -74,4 +76,15 @@ public Boolean testSerializePOJO() {

return struct1.equals(struct2);
}

@GET
@Path("/test")
@Produces("application/fury")
@Consumes("application/fury")
public Bar testBar(Bar obj) {
Preconditions.checkArgument(obj.f1() == 1, obj);
Preconditions.checkArgument(obj.f2().equals("hello bar"), obj);

return new Bar(2, "bye bar");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;

import org.apache.fury.Fury;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.response.Response;

@QuarkusTest
public class FuryTest {
Expand All @@ -24,4 +27,20 @@ public void testPojo() {
public void testThirdPartyBar() {
given().when().get("/fury/third_party_bar").then().statusCode(200).body(is("true"));
}

@Test
public void testFuryBar() {
Bar bar = new Bar(1, "hello bar");
Fury fury = Fury.builder().requireClassRegistration(true).withName("Fury" + System.nanoTime()).build();
fury.register(Bar.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't write code like this, the serialization and deserialization should have same class registration order. If you register Bar here, it allocate an ID x for Bar, but the server allocate id x for ThirdPartyBar. This is why it fails. You could update like this:

@FurySerialization(classId=200)
class Bar {
...
}
fury.register(Bar.class, 200);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's the root cause. Thanks a lot @chaokunyang ! Should we need a NOTE in the documentation or refer to the Apache Fury docs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, would you like submit a pr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean a PR for Fury?

fury.registerSerializer(Bar.class, BarSerializer.class);

Response response = given().contentType("application/fury").body(fury.serialize(bar)).when()
.get("/fury/test").then().statusCode(200).contentType("application/fury").extract().response();

byte[] result = response.body().asByteArray();
Bar bar2 = (Bar) fury.deserialize(result);
Assertions.assertEquals(bar2.f1(), 2);
Assertions.assertEquals(bar2.f2(), "bye bar");
}
}
10 changes: 9 additions & 1 deletion runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.fury</groupId>
<artifactId>fury-core</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package io.quarkiverse.fury;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.MessageBodyReader;
import jakarta.ws.rs.ext.MessageBodyWriter;
import jakarta.ws.rs.ext.Provider;

import org.apache.fury.BaseFury;
import org.apache.fury.Fury;
import org.apache.fury.ThreadSafeFury;
import org.apache.fury.io.FuryInputStream;
import org.apache.fury.resolver.ClassResolver;

import io.quarkus.arc.Arc;

@Provider
@Consumes({ "application/fury", "application/*+fury" })
@Produces({ "application/fury", "application/*+fury" })
public class ClassicFurySerializer implements MessageBodyReader<Object>, MessageBodyWriter<Object> {
private BaseFury fury;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not add @Inject here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, it shoudl work. Let me try.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it does not work. Because resteasy will init the providers at static_init time where the bean container is not running. We have to retrieve BaseFury at runtime. see


public ClassicFurySerializer() {
}

@Override
public boolean isReadable(final Class<?> aClass, final Type type, final Annotation[] annotations,
final MediaType mediaType) {
return isSupportedMediaType(mediaType) && canSerialize(aClass);
}

@Override
public Object readFrom(final Class<Object> aClass, final Type type, final Annotation[] annotations,
final MediaType mediaType, final MultivaluedMap<String, String> multivaluedMap, final InputStream inputStream)
throws WebApplicationException {
return getFury().deserialize(new FuryInputStream(inputStream));
}

@Override
public boolean isWriteable(final Class<?> aClass, final Type type, final Annotation[] annotations,
final MediaType mediaType) {
return isSupportedMediaType(mediaType) && canSerialize(aClass);
}

@Override
public void writeTo(final Object obj, final Class<?> aClass, final Type type, final Annotation[] annotations,
final MediaType mediaType, final MultivaluedMap<String, Object> multivaluedMap, final OutputStream outputStream)
throws IOException, WebApplicationException {
outputStream.write(getFury().serialize(obj));
}

private boolean isSupportedMediaType(MediaType mediaType) {
return mediaType.getType().equals("application") && mediaType.getSubtype().endsWith("fury");
}

private boolean canSerialize(final Class<?> aClass) {
if (getFury() instanceof final ThreadSafeFury threadSafeFury) {
return (threadSafeFury).execute(f -> f.getClassResolver().getRegisteredClassId(aClass)) != null;
} else {
ClassResolver classResolver = ((Fury) getFury()).getClassResolver();
return classResolver.getRegisteredClassId(aClass) != null;
}
}

private BaseFury getFury() {
if (fury == null) {
fury = Arc.container().instance(BaseFury.class).get();
}
return fury;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkiverse.fury;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Produces;

@Dependent
public class ClassicFurySerializerProducer {
@Produces
@ApplicationScoped
public ClassicFurySerializer create() {
return new ClassicFurySerializer();
}
}
Loading