I'm experiencing problem with feign client (spring-boot-starter 2.4.4, spring-cloud-starter-openfeign 3.0.2). When I'm trying to send an empty list inside #ModelAttribute annotated object feign client throws feign.codec.EncodeException with NullPointerException cause. Problem does not occur when list has at least one element.
Does anybody know how to properly override feign encoder to enable passing empty list without errors?
You will want to create a class that implements the feign Encoder (https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/codec/Encoder.java)
e.g.
public class EnableEmptyListEncoder implements Encoder {
#Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
// empty list encode logic here
}
}
How you point to that encoder depends on your setup:
-- via the application.yml:
feign:
client:
config:
feignName:
encoder: com.example.EnableEmptyListEncoder
-- via a buider:
Feign.builder()
.encoder( new EnableEmptyListEncoder() )
-- a bean in the config class
#Bean
public EnableEmptyListEncoder encoder() {
return new EnableEmptyListEncoder();
}
Related
I am using pub sub integration with spring boot, for which my configuration class look like this:
#Configuration
public class PubSubConfiguration {
#Value("${spring.pubsub.topic.name}")
private String topicName;
#Bean
#ServiceActivator(inputChannel = "MyOutputChannel")
public PubSubMessageHandler messageSender(PubSubTemplate pubsubTemplate) {
return new PubSubMessageHandler(pubsubTemplate, topicName);
}
#MessagingGateway(defaultRequestChannel = "MyOutputChannel")
public interface PubsubOutboundGateway {
void sendToPubsub(String attribute);
}
}
So now, I was calling only sendToPubSub method which add payload into topic from my app, like this:
#Autowired
private PubSubConfiguration.PubsubOutboundGateway outboundGateway;
// used line in my code wherever is needed.
outboundGateway.sendToPubsub(jsonInString);
The above code is just meant for one topic which i loaded from application property file.
But now I wanted to make my topic name is dynamically added into messageSender, how to do that.
To override the default topic you can use the GcpPubSubHeaders.TOPIC header.
final Message<?> message = MessageBuilder
.withPayload(msg.getPayload())
.setHeader(GcpPubSubHeaders.TOPIC, "newTopic").build();
and modify your sendToPubsub(Message<byte[]> message) to use message as input.
Refer for more information
Consider creating a BeanFactory to generate a PubSubMessageHandler Bean given a topic name. PubSubMessageHandler also has a setTopic() method, which may be of use.
With Ribbon, if you want to use a custom ServerList implementation instead of the default ConfigurationBasedServerList for a specific service, you can do it like this in the application configuration file:
my-service:
ribbon:
NIWSServerListClassName: com.myapp.MyCustomServerList
My issue is that I want to replace the default ConfigurationBasedServerList for all services I declare to use the MyCustomServerList.
I could just add the previous properties block for each service, but that could grow endlessly.
Is there a way to declare MyCustomServerList as default?
I've also tried adding this bean to my #Configuration class, but it only seems to work the first time I do a request:
#Bean
public ServerList<Server> ribbonServerList() {
return new MyCustomServerList();
}
See http://cloud.spring.io/spring-cloud-static/Dalston.SR1/#_customizing_the_ribbon_client
#RibbonClients(defaultConfiguration=MyConfig.class)
//...
class MyConfig {
#Bean
public ServerList<Server> ribbonServerList() {
return new MyCustomServerList();
}
}
I currently have a REST route builder that looks as follows:
rest("/v1")
.post("/create")
.to("bean:myAssembler?method=assemble(${in.header.content})")
.to("bean:myService?method=create(?)");
The bean myAssembler takes raw JSON and transforms this into MyObject. This object is then returned and I want it forwarded onto myService as a parameter for its create method.
How can I do this using Camel?
Your beans will bind automatically to specific parameters like Exchange if you put it as a parameter to a method (see complete list Parameter binding).
One solution would be to define your route and beans like this:
restConfiguration()
.component("restlet")
.bindingMode(RestBindingMode.json)
.skipBindingOnErrorCode(false)
.port(port);
rest("/v1")
.post("/create")
.route()
.to("bean:myAssembler?method=assemble")
.to("bean:myService?method=create");
with beans like this
public class MyAssembler {
public void assemble(Exchange exchange) {
String content = exchange.getIn().getHeader("content", String.class);
// Create MyObject here.
MyObject object; // ...transformation here.
exchange.getOut().setBody(object);
}
}
and this
public class MyService {
public void create(MyObject body) {
// Do what ever you want with the content.
// Here it's just log.
LOG.info("MyObject is: " + body.toString());
}
}
The dependencies for shown configuration are
org.apache.camel/camel-core/2.15.3
org.apache.camel/camel-spring/2.15.3
org.apache.camel/camel-restlet/2.15.3
javax.servlet/javax.servlet-api/3.1.0
org.apache.camel/camel-jackson/2.15.3
org.apache.camel/camel-xmljson/2.15.3
xom/xom/1.2.5
Actually, if last bean returns MyObject, next bean can accept and bind MyObject as first arg. You don't need to put it into Exchange body or anything.
We have a Spring bean implemented as a singleton (default). This bean is used as part of an web-service, and at times when multiple simultaneous requests are triggered, the responseholder (singleton bean) throws a NullPointerException when trying to retrieve. This usually happens when the response is build, and then a new request is triggered before sending the original response back.
Can this be due to the Singletion implementation of the bean? If yes, is there wouldn't changing to prototype solve my problem. What about initiating it with the new operator always? Will there be any performance impacts on doing so? Or is there a better way.
Any help would be greatly appreciated.
Edit:
Code details
public class BuildToolRequestProcessor {
private BuildToolResponse buildToolResponse;
.....
//And it has been referenced in the code in different methods, setting the response details..
public String process(BuildToolRequestXml buildToolRequestXml) throws Exception {
buildToolResponse.setLocation(location);
...
public String handleDetails(BuildToolRequestXml buildToolRequestXml) throws Exception {
buildToolResponse.setSchedule(schedule);
...
// And in another method, when I try to retrieve the Location, it throws a Null Pointer Exception..
buildToolResponse.getLocation().getPinCode()
//Bean configuration
<bean id="buildToolResponse"
class="com.raj.buildTool.processor.BuildToolResponse"/>
Additional Notes: I tried introducing a delay before bulding the response of the first request, shooting another request. The second request resets the Location to NULL, and hence NPE is thrown while trying to retrieve the Location. Could this be because of the singleton? Also I haven't used initialized the buildToolResponse again with the new operator, but the class BuildToolResponse extends from BuildToolResponseBuilder, which I am initializing using 'new' to build the response.
Can this be due to the Singletion implementation of the bean? If yes, is there wouldn't changing to prototype solve my problem.
If you have a singleton bean, make sure that this bean does not maintain any state. This means, it should not have any field that is reinitialized based on some methods, except for the injection of another beans or resources that is done by Spring. This may cause concurrency issues, specially when the bean is used in several threads (in this case, to attend multiple requests done to your web service).
This is an example of a bad design for a Spring bean that will be used on multiple threads:
#Component
public class SingletonByDefaultBean {
private StringBuilder responseBuilder;
#Autowired
private FooService fooService;
public String methodUsedInSeveralThreads() {
//here you will have a concurrency issue
responseBuilder = new StringBuilder();
//write contents into the response
//...
//return the response
return responseBuilder.toString();
}
}
To solve this, you have two approaches:
Remove any state of the bean and move the attributes into method local variables:
#Component
public class SingletonByDefaultBean {
//private StringBuilder responseBuilder;
#Autowired
private FooService fooService;
public String methodUsedInSeveralThreads() {
StringBuilder responseBuilder = new StringBuilder();
//write contents into the response
//...
//return the response
return responseBuilder.toString();
}
}
Change the scope of the bean to prototype
#Component
#Scope("prototype")
public class SingletonByDefaultBean {
private StringBuilder responseBuilder;
#Autowired
private FooService fooService;
public String methodUsedInSeveralThreads() {
responseBuilder = new StringBuilder();
//write contents into the response
//...
//return the response
return responseBuilder.toString();
}
}
What about initiating it with the new operator always?
Refer to this answer to know how you can create instances your classes manually and make them been managed by Spring. It is not that easy and I would recommend using these approaches only if you really understand what you're doing.
I'm trying to use jersey with my own json MessageBodyReader/MessageBodyWriter (as I am not use #XmlRootElement... annotations on my domain classes).
#Provider
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public final class MyGsonMessageBodyHandler implements MessageBodyWriter<Object>, MessageBodyReader<Object> {
...
}
Jersey uses this class as messagebodywriter (as it stops at breakpoint in the implemented method writeTo). Hovewer it does not see this class as messagebodyreader (and even when I break up this class to the separate implementations of the messagebodyreader/messagebodywriter it still refuses to use my messagebodyreader).
The testing code looks as follows (jersey-grizzly):
final Greeting greeting = resource.path("/greeting")
.queryParam("name", name)
.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.get(Greeting.class);
The error I got looks as follows:
A message body reader for Java class test.Greeting, and Java type class test.Greeting, and MIME media type application/json was not found
I'm wondering what kind of magic is required for writing own MessageBodyReader?
After a while I found a root cause of the issue. My implementation of MessageBodyReader/Writer is OK (and I it works fine with RESTlet), but IF YOU USE JerseyTest, DO NOT FORGET TO ADD YOUR MessageBodyReader/Writer to it's ClientConfig:
/**
* Creates custom REST client config which is mandatory since we don't use any JSON providers.
* #return Jersey Client Config with the required classes to read/write in(out)coming data.
*/
private static ClientConfig createClientConfig() {
final ClientConfig config = new DefaultClientConfig();
config.getClasses().add(GsonMessageBodyHandler.class);
config.getClasses().add(GsonAwareContextResolver.class);
return config;
}
/**
* Public ctor
* #throws com.sun.jersey.test.framework.spi.container.TestContainerException On error
*/
public MyRestExposureTest() throws TestContainerException {
super(new WebAppDescriptor.Builder("my.rest.package")
.clientConfig(createClientConfig())
.contextPath("/")
.build());
}
Otherwise your client code would be unable to read/write your POJOs.
This is what I'm using and it's working (currently with Jersey 1.8).
public static Client createMyClient() {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(MyProviderClass1.class);
cc.getClasses().add(MyProviderClass2.class);
cc.getClasses().add(MyProviderClass3.class);
return Client.create(cc);
}
I'm not sure if it's your case, but the common mistake is incorrect implementation of isReadable method.
Did you implemented it?
Do you stop there, when debugging?
Does it return true?
After trying Alex' solution, this finally worked for me:
public IntegrationTest() throws TestContainerException {
super(new LowLevelAppDescriptor.Builder(createResourceConfig())
.build());
}
private static ResourceConfig createResourceConfig() {
ResourceConfig rc = new PackagesResourceConfig("com.github.joschi.jersey.security.smime");
rc.getSingletons().add(new EnvelopedWriter());
rc.getSingletons().add(new SignedWriter());
return rc;
}