Closed fipro78 closed 8 months ago
I've just given it a try and it seems to work on my machine. Not sure what I doe different to you.
sorry, a rebuild now shows the error.
@timothyjward This seems to be an issue with the class you needed to conjure for the extensions issue with bytebuddy.
I found an Exception that is thrown deep in the bowls of java, when HK2 tries to figure out the advertised Types.
The message from the Exception reads as:
java.lang.reflect.GenericSignatureFormatError: Signature Parse error: Expected Field Type Signature
Remaining input: >;>;Ljakarta/ws/rs/ext/MessageBodyWriter<>;;;
The String it tries to parse is: Ljava/lang/Object;Ljakarta/ws/rs/ext/MessageBodyReader<>;>;Ljakarta/ws/rs/ext/MessageBodyWriter<>;;;
It seems it does not like the <>
here, as it complains about index 55.
The full Stacktrace is:
SignatureParser.parseFieldTypeSignature(boolean) line: 291
SignatureParser.parseFieldTypeSignature() line: 277
SignatureParser.parseTypeArgument() line: 420
SignatureParser.parseTypeArguments() line: 380
SignatureParser.parsePackageNameAndSimpleClassTypeSignature() line: 335
SignatureParser.parseClassTypeSignature() line: 304
SignatureParser.parseSuperInterfaces() line: 544
SignatureParser.parseClassSignature() line: 214
SignatureParser.parseClassSig(String) line: 156
ClassRepository.parse(String) line: 57
ClassRepository.parse(String) line: 41
ClassRepository(AbstractRepository<T>).<init>(String, GenericsFactory) line: 74
ClassRepository(GenericDeclRepository<S>).<init>(String, GenericsFactory) line: 49
ClassRepository.<init>(String, GenericsFactory) line: 53
ClassRepository.make(String, GenericsFactory) line: 70
Class<T>.getGenericInfo() line: 3263
Class<T>.getGenericSuperclass() line: 1016
ReflectionHelper.getAdvertisedTypesFromClass(Type, Class<Annotation>) line: 582
ReflectionHelper.getAdvertisedTypesFromObject(Object, Class<Annotation>) line: 636
BuilderHelper.createConstantDescriptor(T) line: 420
Hk2Helper.translateToActiveDescriptor(InstanceBinding<?>, Type...) line: 312
Hk2Helper.bindBinding(ServiceLocator, DynamicConfiguration, Binding<?,?>) line: 130
Hk2Helper.bind(ServiceLocator, Iterable<Binding>) line: 88
Hk2Helper.bind(AbstractHk2InjectionManager, Binder) line: 66
ImmediateHk2InjectionManager.register(Binder) line: 58
ProviderBinder.bindProvider(Object, ContractProvider, InjectionManager) line: 159
ResourceModelConfigurator.bindProvidersAndResources(InjectionManager, ServerBootstrapBag, ComponentBag, Collection<Class<?>>, Collection<Object>, ResourceConfig) line: 185
ResourceModelConfigurator.init(InjectionManager, BootstrapBag) line: 63
ApplicationHandler.initialize(InjectionManager, List<BootstrapConfigurator>, ServerBootstrapBag) line: 359
ApplicationHandler.lambda$initialize$1(InjectionManager, List, ServerBootstrapBag) line: 310
0x0000000800f06b40.call() line: not available
Errors.process(Callable<T>, boolean) line: 292
Errors.process(Producer<T>, boolean) line: 274
Errors.processWithException(Producer<T>) line: 232
ApplicationHandler.initialize(ApplicationConfigurator, InjectionManager, Binder) line: 309
ApplicationHandler.<init>(Application, Binder, Object) line: 274
WebComponent.<init>(WebConfig, ResourceConfig) line: 311
WhiteboardServletContainer(ServletContainer).reload(ResourceConfig) line: 650
WhiteboardServletContainer.reload(ResourceConfig) line: 108
JerseyServiceRuntime<C>.doDispatch(Map<String,Object>, List<JerseyApplicationProvider>, List<JerseyExtensionProvider>, List<JerseyResourceProvider>) line: 514
JerseyServiceRuntime<C>.doInternalUpdate() line: 373
JerseyServiceRuntime<C>.lambda$scheduleUpdate$0() line: 338
0x0000000800daf9a8.run() line: not available
Executors$RunnableAdapter<T>.call() line: 539
ScheduledThreadPoolExecutor$ScheduledFutureTask<V>(FutureTask<V>).run() line: 264
ScheduledThreadPoolExecutor$ScheduledFutureTask<V>.run() line: 304
ScheduledThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1136
ThreadPoolExecutor$Worker.run() line: 635
Thread.run() line: 833
BTW: In my case, the whole setup runs with java17 here and the class it currently tries to handle is called: org.eclipse.osgitech.rest.proxy.Extension_7fffffff_000000000000003c
and is the wrapper around the JacksonJsonConverter of @fipro78 .
@timothyjward I took the liberty of assigning this to you for now.
After some more testing I noticed that the issue is related to the usage of generics in the extension service interfaces.
The following class declaration fails with the above exception:
public class JacksonJsonConverter<T> implements MessageBodyReader<T>, MessageBodyWriter<T>
If I remove the generics like in the following class declaration, everything works as intended:
public class JacksonJsonConverter implements MessageBodyReader, MessageBodyWriter
The issue also comes up with other supported extension interfaces. I tested with an ExceptionMapper
for verification.
ContextResolver
, ExceptionMapper
, MessageBodyReader
and MessageBodyWriter
are supported extension interfaces with generics. And everyone of them causes an exception if the generic is used in the class definition.
The following is not valid:
public class JacksonJsonConverter<T> implements MessageBodyReader<T>, MessageBodyWriter<T>
You need it to be reified, for example:
public class JacksonJsonConverter implements MessageBodyReader<Object>, MessageBodyWriter<Object>
You can see this is done in the official specification examples
Well, I don't see where it explicitly says that the usage of generics like I did would be invalid. And it worked this way with the old Aries JAX-RS Whiteboard implementation. So I am not sure if the statement "is not valid" is really correct.
But you are right, changing it to the signature you showed resolves the issue.
So I am not sure if the statement "is not valid" is really correct.
Jakarta EE specifications are notoriously woolly, and allow quite divergent behaviours to be considered valid. In this case Jersey (the reference implementation) has a strict requirement to be able to reify the type parameter of an extension to an exact type. Other implementations may be looser, but Jersey sets the standard here (why they can't write these things down I will never know...)
This part of the constructor is for the "holder" of a MessageBodyReader/Writer:
and it calls this part, which requires reification of the type
The interesting thing is, that if I register my converter with the "invalid" class definition via a Jakarta RS Feature, it works.
But anyhow, thanks for your explanations and your time. I will try to include this in my upcoming tutorial about the Jakarta RS Whiteboard.
Feel free to close this issue. I understand that the issue is on the user side. Although it is very unfortunate that the exception is not pointing in the right direction. Or would it be possible to add some checks to provide a better error message that could help a user to solve the issue?
Thinking further about this, one thing did come to mind. When an extension is published to the whiteboard we create a dynamic class for it. This is to allow us to register multiple services even if they are of the same type (which is normally disallowed).
It's possible that in this situation the class we're creating is a valid class, but with generics data that can't be parsed by Jersey.
This is the generic signature we use:
We will need a unit test to be sure...
@fipro78 @juergen-albert @maho7791 - please look at Pull Request #41
I've done my best to think of all possible cases, but generics aren't trivial so if you have other examples to add to the tests then please add them!
@timothyjward Sorry for the late feedback. As you already mentioned, the generics processing is quite complicated, so I only half understood your code you added to fix the issue. But I did some tests with a locally build version from the main branch, and the issues I had before are resolved now. The converter with the following signature is resolved and does not cause the initially reported exception anymore
public class JacksonJsonConverter<T> implements MessageBodyReader<T>, MessageBodyWriter<T>
I am trying to write a blog post about the usage of the Whiteboard Implementation for Jakarta RESTful Web Services. I want it to be similar to my old Build REST services with OSGi JAX-RS whiteboard.
I tried the example by using the Jetty 11 bundles directly instead of the Apache Felix Http Jetty bundle, and with the Apache Felix Http Jetty bundle. In both cases this works fine for a simple service. But when I try to add a converter for JSON, I get the following exception:
I tried various configurations, added bundles explicitly, but nothing works.
On the one side I noticed that Eclipse Parsson is still used in version 1.1.2, but from the changelog it seems that several OSGi related issues were resolved in the meantime. Not sure if this is related, but I opened PR #38 to update the dependency.
Another thing I noticed is that there are many configuration reload executions. At least two of them look strange codewise.
In
JerseyServiceRuntime
line 514 a reload is triggered ifJerseyApplicationProvider#isChanged(Application)
returns true. The interesting thing is, that method always returns true. And that method is not overriding a parent class method, and even the javadoc looks weird. I wonder if this is intended. But of course I am also not sure if this is the cause for the issue.My example is available in GitHub: https://github.com/fipro78/osgi-jakartars
The main branch contains the working example with the simple service setup. The json branch contains the extension with the JSON handling, which shows the error on requesting the resource in a browser.
Once the app is started via the app.bndrun file, you can try to access the resource via http://localhost:8080/modify/fubar, which shows the error in the console.