Consuming More than One SOAP Web Service in Springboot? - java

I've followed this guide to consume my first webservice: https://spring.io/guides/gs/consuming-web-service/
It's ok and working.
But now I need to add another webservice. And I've tried to create another Marshaller configuration file, but when springboot starts it loads only one marshall configuration.
Anyone knows how can I use more than one marshaller?
I've tried to use the same Marshal configuration file as below:
package checkverification.atmncn;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class AtmNcnClientConfiguration {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPaths("checkverification.atmncn.wsdl.test", "checkverification.atmncn.wsdl.live");
return marshaller;
}
#Bean
public AtmNcnLiveClient atmNcnLiveClient(Jaxb2Marshaller marshaller) {
AtmNcnLiveClient client = new AtmNcnLiveClient();
client.setDefaultUri("https://ws.paymentsgateway.net/pg");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
#Bean
public AtmNcnTestClient atmNcnTestClient(Jaxb2Marshaller marshaller) {
AtmNcnTestClient client = new AtmNcnTestClient();
client.setDefaultUri("https://ws.paymentsgateway.net/pgtest");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
But when I'm trying to use the client, I'm receiving a cast exception:
[Internal Server Error]
checkverification.atmncn.wsdl.live.ExecuteSocketDelimitedQueryResponse cannot be cast to
checkverification.atmncn.wsdl.test.ExecuteSocketDelimitedQueryResponse
So I think I need only one marshaller and configure more than one webservice for this marshaller... but I can't figure out how can I do that...
PS: The two webservice has the same methods and classes, but different urls (one for test and other for production). So maybe it's causing the cast exception...
My working class is a controller, it's very long so I will paste only the main code:
#RestController
#RequestMapping("/cvapi")
public class TransactionApiController {
#Autowired
AtmNcnTestClient atmNcnTestClient;
#Autowired
AtmNcnLiveClient atmNcnLiveClient;
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Object> submit(#RequestBody #Valid Transaction transaction, HttpServletRequest request) {
// a lot of code with the controller logic...
// Do a Test Post
if(as.getTestMode() || licenseNumberType == VerifyType.TEST) {
checkverification.atmncn.wsdl.test.ExecuteSocketDelimitedQueryResponse res =
atmNcnTestClient.executeSocketDelimitedQueryResponseTest(params); // ---->>> ERROR IN THIS LINE
atmNcnResultWrapper = AtmNcnParameterBuilder.getResult(
res.getExecuteSocketDelimitedQueryResult());
} else {
// Live Post
checkverification.atmncn.wsdl.live.ExecuteSocketDelimitedQueryResponse res =
atmNcnLiveClient.executeSocketDelimitedQueryLive(params);
atmNcnResultWrapper = AtmNcnParameterBuilder.getResult(
res.getExecuteSocketDelimitedQueryResult());
}
// a lot of code to generate the response
} // end of method
}

Related

Update: Spring Boot JMS static reply queue on IBM MQ Series

In my use case I need to do request-reply call to a remote system via managed queues. Using Spring Boot and IBM's MQ starter I have the problem that the application wants to create dynamic/temporary reply queues instead of using the already existing managed queue.
Configuration is set up here
#EnableJms
#Configuration
public class QueueConfiguration {
#Bean
public MQQueueConnectionFactory connectionFactory() throws JMSException {
MQQueueConnectionFactory factory = new MQQueueConnectionFactory();
factory.setTransportType(CT_WMQ); // is 1
factory.setHostName(queueProperties.getHost());
factory.setPort(queueProperties.getPort());
factory.setChannel(queueProperties.getChannel()); // combo of ${queueManager}%${channel}
return factory;
}
#Bean
public JmsMessagingTemplate messagingTemplate(ConnectionFactory connectionFactory) {
JmsMessagingTemplate jmt = new JmsMessagingTemplate(connectionFactory);
jmt.setDefaultDestinationName(queueProperties.getQueueName());
return jmt;
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.foo.model");
return marshaller;
}
#Bean
public MessageConverter messageConverter(Jaxb2Marshaller marshaller) {
MarshallingMessageConverter converter = new MarshallingMessageConverter();
converter.setMarshaller(marshaller);
converter.setUnmarshaller(marshaller);
return converter;
}
}
Usage is pretty straight forward: Take the object convert and send it. Wait for response receive
and convert it.
#Component
public class ExampleSenderReceiver {
#Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
#Override
#SneakyThrows
public ResponseExample sendAndReceive(RequestExample request, String correlationId) {
MessagePostProcessor mpp = message -> {
message = MessageBuilder.fromMessage(message)
.setHeader(JmsHeaders.CORRELATION_ID, correlationId)
// .setHeader(JmsHeaders.REPLY_TO, "DEV.QUEUE.3") this triggers queue creation
.build();
return message;
};
String destination = Objects.requireNonNull(jmsMessagingTemplate.getDefaultDestinationName());
return jmsMessagingTemplate.convertSendAndReceive(destination, request, ResponseExample.class, mpp);
}
I read already a lot of IBM documentation and think, I need to set the message type to "MQMT_REQUEST" but I do not find the right spot to do so.
Update
Added Spring Integration as Gary proposed and added a configuration for JmsOutboundGateway
#Bean
public MessageChannel requestChannel() {
return new DirectChannel();
}
#Bean
public QueueChannel responseChannel() {
return new QueueChannel();
}
#Bean
#ServiceActivator(inputChannel = "requestChannel" )
public JmsOutboundGateway jmsOutboundGateway( ConnectionFactory connectionFactory) {
JmsOutboundGateway gateway = new JmsOutboundGateway();
gateway.setConnectionFactory(connectionFactory);
gateway.setRequestDestinationName("REQUEST");
gateway.setReplyDestinationName("RESPONSE");
gateway.setReplyChannel(responseChannel());
gateway.setCorrelationKey("JMSCorrelationID*");
gateway.setIdleReplyContainerTimeout(2, TimeUnit.SECONDS);
return gateway;
}
And adapted my ExampleSenderReceiver class
#Autowired
#Qualifier("requestChannel")
private MessageChannel requestChannel;
#Autowired
#Qualifier("responseChannel")
private QueueChannel responseChannel;
#Override
#SneakyThrows
public ResponseExample sendAndReceive(RequestExample request, String correlationId) {
String xmlContent = "the marshalled request object";
Map<String, Object> header = new HashMap<>();
header.put(JmsHeaders.CORRELATION_ID, correlationId);
GenericMessage<String> message1 = new GenericMessage<>(xmlContent, header);
requestChannel.send(message1);
log.info("send done" );
Message<?> receive = responseChannel.receive(1500);
if(null != receive){
log.info("incoming: {}", receive.toString());
}
}
The important part is gateway.setCorrelationKey("JMSCorrelationID*");
Without that line the correlationId was not propagated correct.
Next step is re-adding MessageConverters and make it nice again.
Thank you.
The default JmsTemplate (used by the JmsMessagingTemplate) always uses a temporary reply queue. You can subclass it and override doSendAndReceive(Session session, Destination destination, MessageCreator messageCreator) to use your managed queue instead.
However, it will only work if you have one request outstanding at a time (e.g. all run on a single thread). You will also have to add code for discarding "late" arrivals by checking the correlation id.
You can use async sends instead and handle replies on a listener container and correlate the replies to the requests.
Consider using spring-integration-jms and its outbound gateway instead - it has much more flexibility in reply queue handling (and does all the correlation for you).
https://docs.spring.io/spring-integration/reference/html/jms.html#jms-outbound-gateway
You are missing the queue manager.
ibm:
mq:
queueManager: QM1
channel: chanel
connName: localhost(1414)
user: admin
password: admin

Feign client always null when being added through a library, how can I fix it?

I'have lately working with feign on a project where I decided will package it and ship it as a library so it will be easier to use. All things worked fine but at the part where I invoke the feign client it just returns a NPE.
I am wondering if Im missing a configuration, or this is because the fact its a library? or how it is included in the project?
Basically I have something like this (in the library):
On a LibraryConfiguration.java
//some more beans
#Bean
public ClientService clientService(){
return new ClientService();
}
at ClientService I have
#Autowired
private MyClient myClient;
ClientService(){
myClient.myResource();
}
At MyClient:
#FeignClient(name = "auth", url = "${my-url}", configuration = MyClient.Configuration.class)
public interface MyClient {
#RequestMapping(method = RequestMethod.POST, value = "/my-resource", consumes = "application/x-www-form-urlencoded")
String getResource(Map<String, ?> formParams);
class Configuration {
#Bean
Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> converters) {
return new SpringFormEncoder(new SpringEncoder(converters));
}
}
}
So it was all packaged and shipped.
At my app I have:
#SpringBootApplication
#Import(LibraryConfiguration.class)
#EnableFeignClients()
#Configuration
#ImportAutoConfiguration(FeignAutoConfiguration.class)
public class MyApiApplication {
....
}
I added the library like this (Im using gradle)
compile 'com.my-library:version'
Any idea of what could be missing?
Thanks.
Make sure the package level of the feign client is below the package level of the Main class(MyApiApplication).

Spring boot & Java - HTTP Status 404 error aka white-label error

Please have a look at my codes below. The Java codes seemed to work just fine, but localhost:8080 gives me the error code 404 when I try to access it. I want to make localhost 8080 work. Please let me know if you need further information.
Application
#SpringBootApplication(exclude = { ErrorMvcAutoConfiguration.class })
// exclude part is to elimnate whitelabel error
#EnableScheduling
public class Covid19TrackerApplication {
public static void main(String[] args) {
SpringApplication.run(Covid19TrackerApplication.class, args);
}
}
Controller
#Controller
public class HomeController {
CovidDataService covidDataService;
#RequestMapping("/")
public #ResponseBody String home(Model model) {
model.addAttribute( "locationStats", covidDataService.getAllStats());
return "home";
}
}
Main Code
#Service
public class CovidDataService {
private static String Covid_Data_URL = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv";
private List<LocationStats> allStats = new ArrayList<>();
public List<LocationStats> getAllStats() {
return allStats;
}
#PostConstruct//?
#Scheduled(cron = "* * 1 * * *") //????
// * sec * min *hour and so on
public void fetchCovidData() throws IOException, InterruptedException {
List<LocationStats> newStats = new ArrayList<>(); // why we are adding this? To prevent user get an error while we are working on new data.
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(Covid_Data_URL))
.build(); // uri = uniform resource identifier
HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
StringReader csvBodyReader = new StringReader(httpResponse.body()); //StringReader needs to be imported
Iterable<CSVRecord> records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(csvBodyReader); // parse(in) had error, we needed a "reader" instance.
for (CSVRecord record : records) {
LocationStats locationStat = new LocationStats(); //create an instance
locationStat.setState(record.get("Province/State"));
locationStat.setCountry(record.get("Country/Region"));
locationStat.setLatestTotalCase(Integer.parseInt(record.get(record.size()-1)));
System.out.println(locationStat);
newStats.add(locationStat);
}
this.allStats = newStats;
}
}
The problem may come from this piece of code
#RequestMapping("/")
public #ResponseBody String home(Model model) {
model.addAttribute( "locationStats", covidDataService.getAllStats());
return "home";
}
it returns "home" which should be existing view, normally, the view will be a jsp file which is placed somewhere in WEB-INF, please see this tutorial: https://www.baeldung.com/spring-mvc-view-resolver-tutorial
In the case of wrong mapping, it may returns 404 error
when you run the server, you should be able to see which port it's taken in the console.
Also, is server.port=8080 in the src/main/resources/application.properties file?
In the controller, the RequestMapping annotation is missing the method type and header
#RequestMapping(
path="/",
method= RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_VALUE)
public String home(Model model) {
model.addAttribute( "locationStats", covidDataService.getAllStats());
return "home";
}
make sure to add consumes for POST or PUT methods
A bit unrelated to the question but the line in the controller is missing #Autowired annotation
CovidDataService covidDataService;
Preferrably, add the #Autowired in the constructor
#Autowired
public HomeController(CovidDataService covidDataService) {
this.covidDataService = covidDataService;
}

Spring Webservice Client - Empty soapAction returning in response "Message part Request was not recognized"

I am using spring webservices for consuming a service. I am following this article https://spring.io/guides/gs/consuming-web-service/, I have created a client by extending WebServiceGatewaySupport and using that to invoke the service.
The first doubt I have is that I haven't generated any java classes from wsdl (other than jaxb objects for the types mentioned in wsdl). Don't I have to generate the stub (endpoint) classes on the client side? If not, then how does spring know which operation to be invoked as my wsdl has multiple operations?
Here is my code
public class SOAPConnector extends WebServiceGatewaySupport {
public Object callWebService(String url, Object request){
return getWebServiceTemplate().marshalSendAndReceive(url, request);
}
}
#Configuration
public class Config {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this is the package name specified in the <generatePackage> specified in
// pom.xml
marshaller.setContextPath("com.example.howtodoinjava.schemas.school");
return marshaller;
}
#Bean
public SOAPConnector soapConnector(Jaxb2Marshaller marshaller) {
SOAPConnector client = new SOAPConnector();
client.setDefaultUri("https://www.example.com/ExampleServiceBean");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
The second issue is that on executing above code I am getting following error message, can someone please help me how to fix it:
org.springframework.ws.soap.client.SoapFaultClientException: Message part Request was not recognized. (Does it exist in service WSDL?)
at org.springframework.ws.soap.client.core.SoapFaultMessageResolver.resolveFault(SoapFaultMessageResolver.java:38)
at org.springframework.ws.client.core.WebServiceTemplate.handleFault(WebServiceTemplate.java:830)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:624)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:506)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:446)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:436)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:424)
I might be a missing a very basic thing as I am using spring webservices for the first time. Thanks in advance.

SOAP WS-Addressing property with Wss4jSecurityInterceptor with Java

Hi I create code for consume SOAP service,
For Authentication Header I used Wss4jSecurityInterceptor for set Header information.
I am getting fail response like below
Exception in thread "main" org.springframework.ws.soap.client.SoapFaultClientException: Required element {http://www.w3.org/2005/08/addressing}Action is missing
My Configuration code as below
#Configuration
public class SoapClientConfig {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.xyz.client");
marshaller.setCheckForXmlRootElement(false);
return marshaller;
}
#Bean
public MyClient myClient(Jaxb2Marshaller marshaller) throws Exception {
MyClient client = new MyClient();
client.setDefaultUri("https://localhost:8080/ws/service");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
ClientInterceptor[] interceptors = new ClientInterceptor[] {securityInterceptor()};
client.setInterceptors(interceptors);
return client;
}
#Bean
public Wss4jSecurityInterceptor securityInterceptor() {
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
wss4jSecurityInterceptor.setSecurementActions("UsernameToken");
wss4jSecurityInterceptor.setSecurementMustUnderstand(true);
wss4jSecurityInterceptor.setSecurementPasswordType("PasswordText");
wss4jSecurityInterceptor.setSecurementUsername("XXXXXXXXXXX");
wss4jSecurityInterceptor.setSecurementPassword("XXXXXXXX");
return wss4jSecurityInterceptor;
}
}
Can anyone suggest me what I am missing?
If I try from SOAPUI its working fine. If I set WS-Addressing=false from SOAPUI also giving me same error, So Issue with set WS-Addressing property with above code. How can I?
Do you use WebServiceTemplate to send the request? If yes, you can do something like :
ActionCallback callback = new ActionCallback(
new URI("action uri"));
Here you should provide actual uri location of action instead "action uri". Then, do
getWebServiceTemplate().marshalSendAndReceive(request, callback)
Long time before worked on populating SOAP Header with dynamic value, for that you need to work on constructing the xml nodes using callback object...WebServiceMessageCallback
http://docs.spring.io/spring-ws/site/reference/html/client.html#d5e1848
In my scenario I need to construct the node using QName (Java) Node by Node.

Categories

Resources