Unable to get CDI and JAX-RS to work together in Glassfish - glassfish

I have to deploy a project that was working successfully in Wildfly to Glassfish. Everything worked fine except for dependency injection of an object in ResponseFilter. The object is Produced in the REST resource handler. I have created a simple project to demonstrate the issue. I have gone through several answers on Stackoverflow. Tried everything and none seem to work.
bean.xml
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
App.java
#ApplicationPath("api")
public class App extends Application {
}
DemoResource.java
#Path("/demo")
public class DemoResource {
#Produces
private Pojo pojo;
#GET
public void demo() {
pojo = new Pojo();
}
}
Pojo.java
#Alternative
public class Pojo {
protected String firstName;
protected String lastName;
}
ResponseFilter.java
#Provider
public class ResponseFilter implements ContainerResponseFilter {
#Inject
private Pojo injectedPojo;
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
Instance<Pojo> instance = CDI.current().select(Pojo.class);
Pojo uninjectedPojo = null;
if (!instance.isUnsatisfied() && instance.get() != null)
uninjectedPojo = instance.get();
System.out.println("Injected POJO: " + injectedPojo);
System.out.println("Uninjected POJO: " + uninjectedPojo);
}
}
The output in Wildfly log file is:
16:33:58,765 INFO [stdout] (default task-3) Injected POJO: com.example.demo.Pojo#2092c78b
16:33:58,765 INFO [stdout] (default task-3) Uninjected POJO: com.example.demo.Pojo#2092c78b
The output in Glassfish log is:
[2018-02-14T16:36:37.730+0545] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(3)] [timeMillis: 1518605497730] [levelValue: 800] [[Injected POJO: null]]
[2018-02-14T16:36:37.730+0545] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(3)] [timeMillis: 1518605497730] [levelValue: 800] [[Uninjected POJO: null]]

Add <alternatives> declaration to the beans.xml
<beans ... >
<alternatives>
<class>[package name].Pojo</class>
</alternatives>
</beans>
Or add
#Priority(Interceptor.Priority.APPLICATION+100)
anotion to Pojo class

Related

Quarkus 2 vs JUnit TestInfo: classloader issue on annotated test

In order to make some configurations, I'm reading annotations on tests using JUnit TestInfo.
However I'm facing a problem on Quarkus 2 since TestInfo is loaded with a different classloader than my test, so looking for annotations using testInfo.getTestMethod()...getAnnotationsByType does not return anything.
Consider:
#QuarkusTest
#MyTestConfig("class")
public class AnnotatedTest {
#Test
#MyTestConfig("method")
void test(TestInfo testInfo) {
Class<?> declaringClass = testInfo.getTestMethod().orElseThrow().getDeclaringClass();
System.out.println("declaringClass classloader: " + declaringClass.getClassLoader());
System.out.println("this classloader: " + this.getClass().getClassLoader());
}
}
This gives (Quarkus 2.1.2.Final):
declaringClass classloader: jdk.internal.loader.ClassLoaders$AppClassLoader#3b192d32
this classloader: QuarkusClassLoader:Quarkus Runtime ClassLoader: TEST restart no:0#6a9ad3d6
On Quarkus 1.13.7 both are:
declaringClass classloader: QuarkusClassLoader:Quarkus Base Runtime ClassLoader
this classloader: QuarkusClassLoader:Quarkus Base Runtime ClassLoader
As a consequence, this doesn't work:
#QuarkusTest
#MyTestConfig("class")
public class AnnotatedTest {
#Test
#MyTestConfig("method")
void testProcessAnnotation(TestInfo testInfo) {
assertThat(processAnnotation(testInfo, MyTestConfig.class))
.extracting(MyTestConfig::value).containsExactly("class", "method");
}
<T extends Annotation> List<T> processAnnotation(TestInfo testInfo, Class<T> annotationClass) {
Method m = testInfo.getTestMethod().orElseThrow();
T[] classAnnotations = m.getDeclaringClass().getAnnotationsByType(annotationClass);
T[] methodAnnotations = m.getAnnotationsByType(annotationClass);
return Stream.of(classAnnotations, methodAnnotations)
.flatMap(Arrays::stream)
.collect(Collectors.toList());
}
}

Cannot inject List<String> for class extending Java Class

Getting below error while trying to Inject List of String into a class constructor. I am extending AbstractDelegator java class as CancelDelegator. AbstractDelegator is referenced from a dependency jar so I can't change that.
No implementation for java.util.List<java.lang.String> annotated with #com.google.inject.name.Named(value=CANCEL_TASKS) was bound.
while locating java.util.List<java.lang.String> annotated with #com.google.inject.name.Named(value=CANCEL_TASKS)
for parameter 1 at com.cancel.core.delegator.CancelDelegator.<init>(CancelDelegator.kt:18).
at com.cancel.core.config.CancelModule.configure(CancelModule.kt:45)
Following are the classes:
AbstractDelegator.java ( from dependency jar)
public abstract class AbstractDelegator<T extends Request, R extends Response> implements Delegator<T, R> {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected Processor<T, R> processor;
protected List<String> taskSequence;
protected AbstractDelegator(Processor<T, R> processor, List<String> taskSequence) {
this.processor = processor;
this.taskSequence = taskSequence;
}
}
CancelDelegator.kt
#Singleton
class CancelDelegator #Inject constructor(
#Named(CancelConstant.CANCEL_PROCESSOR) processor: Processor<Request?, Response?>?,
#Named(CancelConstant.CANCEL_TASKS) taskSequence: List<String?>?
) : AbstractDelegator<Request?, Response?>(processor, taskSequence)
GuiceModule.kt
bind<Delegator<Request?, Response?>?>(object : TypeLiteral<Delegator<Request?, Response?>?>() {})
.annotatedWith(Names.named(CancelConstant.CANCEL_DELEGATOR))
.to(CancelDelegator::class.java)
bind<List<String?>?>(object : TypeLiteral<List<String?>?>() {})
.annotatedWith(Names.named(CancelConstant.CANCEL_TASKS))
.toInstance(Arrays.asList(CancelConstant.CANCEL_PENALTY_TASK))
Its only the List that Guice is not able to inject. Other injections are working fine. I have tried changing List<String?>? to List<String?> and List but nothing seems to work.
Like explained in this GitHub issue,
var setA: Set<Runnable>
var setB: MutableSet<Runnable>
is equivalent to the following in Java:
Set<? extends Runnable> setA;
Set<Runnable> setB;
You can fix it with the #JvmSuppressWildcards annotation. Something like the following:
bind(object : TypeLiteral<List<#JvmSuppressWildcards String>>() {})
.annotatedWith(Names.named(CancelConstant.CANCEL_DELEGATOR))
.toInstance(listOf("hello", "goodbye"))

Error with Parcelable / Parceler when minifyEnabled true

I am getting the following error when I set minifyEnabled true:
Unable to find generated Parcelable class for com.codeworks.myapp.FirestoreModel, verify that your class is configured properly and that the Parcelable class com.codeworks.myapp.FirestoreModel$$Parcelable is generated by Parceler.
I placed the following code as shown in the parceler.org website in proguard-rules.pro:
# Parceler configuration
-keep interface org.parceler.Parcel
-keep #org.parceler.Parcel class * { *; }
-keep class **$$Parcelable { *; }
-keep class org.parceler.Parceler$$Parcels
... and added the #Keep annotation in the FirestoreModel and the Fragment's class:
#Parcel
#Keep
class FirestoreModel{
...
}
#Keep
class MyFragment: Fragment() {
...
}
and then added some pro-guard rules, or as I understood it from the examples from stackoverflow.com (because I can't understand the rules from official documentation):
-keepnames class com.codeworks.myapp.MyFragment { *; }
-keepnames class com.codeworks.myappFirestoreModel { *; }
I am still getting the following error:
2020-04-21 08:07:38.554 28188-28188/com.codeworks.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.codeworks.myapp, PID: 28188
h.a.d: Unable to find generated Parcelable class for com.codeworks.myappFirestoreModel, verify that your class is configured properly and that the Parcelable class com.codeworks.myappFirestoreModel$$Parcelable is generated by Parceler.
at h.a.e$b.a(:154)
at h.a.e.a(:73)
at h.a.e.a(:57)
at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:103)
at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:68)
at com.firebase.ui.firestore.FirestoreRecyclerAdapter.b(:158)
at androidx.recyclerview.widget.RecyclerView$g.a(:7065)
at androidx.recyclerview.widget.RecyclerView$g.a(:7107)
at androidx.recyclerview.widget.RecyclerView$v.a(:6012)
at androidx.recyclerview.widget.RecyclerView$v.a(:6279)
at androidx.recyclerview.widget.RecyclerView$v.b(:6118)
at androidx.recyclerview.widget.RecyclerView$v.d(:6114)
at androidx.recyclerview.widget.LinearLayoutManager$c.a(:2303)
at androidx.recyclerview.widget.LinearLayoutManager.a(:1627)
at androidx.recyclerview.widget.LinearLayoutManager.a(:1587)
at androidx.recyclerview.widget.LinearLayoutManager.e(:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(:3851)
at androidx.recyclerview.widget.RecyclerView.consumePendingUpdateOperations(:1897)
at androidx.recyclerview.widget.RecyclerView$a.run(:414)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:727)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7464)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:955)
Apparently, the variable names of the onViewCreated function on MyFragment are being renamed despite the pro-guard rules.
The error seems to point to this lines:
// Here... at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:68)
adapter = object : FirestoreRecyclerAdapter<DpMemoModel, DpViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FirestoreViewHolder{
val view : View = LayoutInflater.from(parent.context).inflate(R.layout.firestore_cardview, parent, false)
return FirestoreViewHolder(view)
}
override fun onBindViewHolder(
holder: FirestoreViewHolder,
position: Int,
model: FirestoreModel
) {
...
// ...and Here: **at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:103)**
val wrapped : Parcelable = Parcels.wrap(model)
holder.cardView.setOnClickListener {
val intent= Intent(activity, FirestoreInfoActivity::class.java)
intent.putExtra("parcel", wrapped)
startActivity(intent)
}
P.S.: I am using FirestoreRecyclerAdapter, I thought it was the best way since I was dealing with Cloud Firestore and Firebase Storage data.
I don't know what went wrong or what I've missed. I switched to #Parcelize for now. The code is a bit longer, but at least it works. Also, there is no need to use:
# Parceler configuration
-keep interface org.parceler.Parcel
-keep #org.parceler.Parcel class * { *; }
-keep class **$$Parcelable { *; }
-keep class org.parceler.Parceler$$Parcels
...in the proguard-rules.pro. All I need is #Keep annotation in the data class. I've spent a day and a half on research and trial and error, but, well, c'est la vie.

Spring webflux error handler: How to get the reactor context of the request in the error handler?

Spring boot 2.1.5 Project Reactor 3.2.9
In my webflux project, I extensively use the reactor contexts in order to pass around some values.
My purpose here is to be able to get the context inside of the Exception handler.
A simple example:
#Component
#Order(-2)
public class GlobalErrorWebExceptionHandler extends
AbstractErrorWebExceptionHandler {
public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ApplicationContext applicationContext, ServerCodecConfigurer configurer) {
super(errorAttributes, resourceProperties, applicationContext);
this.setMessageWriters(configurer.getWriters());
}
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(
ErrorAttributes errorAttributes) {
return RouterFunctions
.route(RequestPredicates.all(), request -> {
Throwable error = errorAttributes.getError(request);
return ServerResponse.status(500).syncBody(error.getMessage()).doOnEach(serverResponseSignal -> {
//Here the context is empty because i guess i created a new flux
System.out.println("What is in my context ? " + serverResponseSignal.getContext());
System.out.println("What is my exception ? " + error);
});
});
}
}
I am not sure how to achieve that goal in a clean way with reactor. Anyone an idea ?
I found a trick to be able to achieve that. It does not sound clean but it seems to work.
In a filter, I keep the subscribed context into a request attribute:
#Component
public class MdcWebFilter implements WebFilter {
#NotNull
#Override
public Mono<Void> filter(#NotNull ServerWebExchange serverWebExchange,
WebFilterChain webFilterChain) {
Mono<Void> filter = webFilterChain.filter(serverWebExchange);
return filter
.subscriberContext((context) -> {
//This code is executed before the query
Context contextTmp = context.put("whatever", "whichever");
//I save the context in an attribute attribute
serverWebExchange.getAttributes().put("context", contextTmp);
return contextTmp;
});
}
}
Then after that it is possible to get it from the reactive error handler:
#Component
#Order(-2)
public class GlobalErrorWebExceptionHandler extends
AbstractErrorWebExceptionHandler {
public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ApplicationContext applicationContext, ServerCodecConfigurer configurer) {
super(errorAttributes, resourceProperties, applicationContext);
this.setMessageWriters(configurer.getWriters());
}
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(
ErrorAttributes errorAttributes) {
return RouterFunctions
.route(RequestPredicates.all(), request -> {
Throwable error = errorAttributes.getError(request);
//The context will be visible in the whole error handling flow
return ServerResponse.status(500).syncBody(error.getMessage())
.subscriberContext((Context) request.attribute("context").orElse(Context.empty())));
});
}
}

JAX-RS ExceptionMapper throws MessageBodyProviderNotFoundException

Using JAX-RS, I have successfully implemented an ExceptionMapper for Exceptions that do not require a more sophisticated response than an HTTP status code, as follows.
#Provider
public class ISBNNotFoundManager implements ExceptionMapper<ISBNNotFoundException>{
#Override
public Response toResponse(ISBNNotFoundException exception) {
return Response.status(NOT_FOUND).build();
}
}
This works as expected.
However, I want to respond with something more useful when bean validation fails. The follow code snippet results in a MessageBodyProviderNotFoundException.
#Provider
public class ConstraintViolationExceptionMapper implements
ExceptionMapper<ConstraintViolationException> {
#Override
#Produces(MediaType.APPLICATION_JSON)
public Response toResponse(ConstraintViolationException exception) {
final Map<String, String> errorResponse =
exception.getConstraintViolations()
.stream()
.collect(
Collectors.toMap(o -> o.getPropertyPath().toString(), o -> o.getMessage()));
return Response.status(Response.Status.BAD_REQUEST).entity(errorResponse).build();
}
}
When a bean validation occurs the response includes the HTTP response code 500 and the root cause is given as follow:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException:
MessageBodyWriter not found for media type=application/json,
type=class java.util.HashMap, genericType=class java.util.HashMap.
What I have tried that didn't work:
Wrapping the Map in a GenericEntity like so. The same result as above:
new GenericEntity>(errorResponse) {}
What I tried the DID work:
Wrapping the map in a custom POJO, DataIntegrityValidation, as follows:
#XmlRootElement
public class DataIntegrityValidation {
private Map<String, String> errorResponse = new HashMap<>();
public Map<String, String> getErrorResponse() {
return errorResponse;
}
public void setErrorResponse(Map<String, String> errorResponse) {
this.errorResponse = errorResponse;
}
}
Then in the toResponse method I wrap the map in the DataIntegrityValidation POJO like so and add it to the response object.
DataIntegrityValidation dataIntegrityValidation =
new DataIntegrityValidation();
dataIntegrityValidation.setErrorResponse(errorResponse);
return
Response.status(Response.Status.BAD_REQUEST)
.entity(dataIntegrityValidation).build();
This gives the following JSON:
{
"errorResponse": {
"entry": [
{
"key": "saveBook.arg0.description",
"value": "size must be between 100 and 2147483647"
},
{
"key": "saveBook.arg0.published",
"value": "must be in the past"
},
{
"key": "saveBook.arg0.link",
"value": "must match \"^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$\""
}
]
}
}
I can live with this but would really like to know why it cannot handle the Map even though it is wrapped in the Generic Entity.
All responses welcome.
The reason the marshalling failed for both Map and GenericEntity is because there is no JAXB definition associated with them. And when you wrapped the map in a POJO annotated with #XmlRootElement; it was able to marshal it correctly.