I am able to call soap service and getting the response from soap client does means connectivity is okay. But the request data values are not mapping to external system request. and we both are using same package and class names.
Please find the code as below:
UserListResponse response = null;
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "UserList")
#ResponsePayload
public UserListResponse UserListRequest(#RequestPayload UserListRequest request) throws Exception {
System.out.println("Enters into UserList()");
try {
//Client call
UserServicesLocator locator = new UserServicesLocator();
UserServicesSoapStub stub = (UserServicesSoapStub) locator.getUserServicesSoap();
response = stub.userList(request);//here the request data values not mapping at external system side
} catch(Exception e) {
e.printStackTrace();
}
return response;
}
Note: Client classes are able to generated using wsdl and in that client classes having pojo structure with serialization and de-sterilization methods.
Can anyone please suggest on this issue.
Related
I am making rest template call to get the data from other microservice for this I am using the exchange method. This I am doing when a particular function gets called and below is the sample code for the same.
#Service
public void findUserById()
{
String username = "chathuranga";
String password = "123";
Integer userId = 1;
String url = "http://localhost:8080/users/" + userId;
//setting up the HTTP Basic Authentication header value
String authorizationHeader = "Basic " + DatatypeConverter.printBase64Binary((username + ":" + password).getBytes());
HttpHeaders requestHeaders = new HttpHeaders();
//set up HTTP Basic Authentication Header
requestHeaders.add("Authorization", authorizationHeader);
requestHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
//request entity is created with request headers
HttpEntity<AddUserRequest> requestEntity = new HttpEntity<>(requestHeaders);
ResponseEntity<FindUserResponse> responseEntity = restTemplate.exchange(
url,
HttpMethod.GET,
requestEntity,
FindUserResponse.class
);
// if (responseEntity.getStatusCode() == HttpStatus.OK) {
// System.out.println("response received");
System.out.println(responseEntity.getBody());
//} else {
// System.out.println("error occurred");
// System.out.println(responseEntity.getStatusCode());
//}
}
To handle the various exceptions code for example 500, 404 I want to made resttemplate builder class, (not the commented code) Which must be coded in different class for this I am referring this (custom hadler part)
I am not using try catch as it is not good approach when multiple calls happen in production environment.
I am also getting resource access exception while using exchange function which also needs to handle.
Now I am not getting how this class of custom handler should be called for handling response like 500.
If someone can help me with the sample code that would be very helpfull as I cannot test my code because it is not deployed for testing purpose till now
here is a sample
#ControllerAdvice
public class ErrorHandler {
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(ResourceAccessException.class)
public #ResponseBody
String handleResourceAccessException(
ResourceAccessException ex) {
return "internal server error";
}
}
When you use #ControllerAdvice , it will catch the exception you mention in #ExceptionHandler and here you can handle it the way you want.
If you don't want to return the response to the client right away, (for example, ignore ResourceAccessException and continue), you can override the handleError method of DefaultResponseErrorHandler, which is used by RestTemplate to handle the non 2xx codes.
public class ErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response, HttpStatus statusCode) {
// write your code here
}
}
I have a Feign client with a method returning the feign.Response class. When another service throws an exception, feign puts an exception message on response body and puts status, but my service does not throw an exception. Can I throw an exception based on what I received in response like when I use ResponseEntity.
Feign client
#FeignClient(name = "ms-filestorage")
#RequestMapping(value = "/files", produces = "application/json")
public interface FileStorageApi {
#GetMapping(value = "/{id}")
Response getFileById(#PathVariable String id);
}
Usage of client
#Override
public Response getFileFromStorage(String fileId) {
Response fileStorageResponse = fileStorageApi.getFileById(fileId);
// NOW I USE THIS WAY FOR CHECKING RESPONSE BUT IT DOESN'T LOOK GOOD
//if (fileStorageResponse.status() != HttpStatus.OK.value()) {
// throw new OsagoServiceException();
//}
return fileStorageResponse;
}
Usually, if a Feign client call receives an error response from the API it is calling, it throws a FeignException.
This can be caught in a try / catch block (or a Feign ErrorDecoder if you want to be more sophisticated, but that's another post).
However, this is not the case if you map the error response into a Feign.Response return type - see this Github issue.
Instead of returning Feign.Response from getFileFromStorage(), you should create a custom Java object to hold the response, and you will then have access to the FeignException which you can handle as you wish.
Note that if you don't need access to the data that is returned from the API you are calling, changing the return type to void will also resolve this issue.
I'm using ResteasyClient to make a REST client to my another service A. Say, service A throws an exception CustomException with a message : Got Invalid request.
Here is how I am using the Client:
public Response callServiceA(final String query) {
ResteasyClient client = new ResteasyClientBuilder().build();
String link = "abc.com/serviceA";
ResteasyWebTarget target = client.target(link).path("call");
Form form = new Form();
form.param("query", query);
Response response;
try {
String data =
target.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE),
String.class);
response = Response.ok().entity(data).build();
} catch (Exception e) {
String message = e.getMessage();
e.printStackTrace();
response = Response.serverError().entity(e.getMessage()).build();
} finally{
client.close();
}
return response;
}
However, when I print the stacktrace, I'm unable to find my custom error message. I can just see the message as HTTP 400 Bad Request.
Could you please suggest me how to access the error message?
NOTE: I am able to get the error message when I call the serviceA using the restClient. Hence, I dont think there is an issue with the service.
Don't deserialize the response straight to a String.
String data = ...
.post(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE),
String.class);
When you do this (and there is a problem), you just get a client side exception, which doesn't carry information about the response. Instead just get the Response with the overloaded post method
Response response = ...
.post(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE));
Then you can get details from the Response, like the status and such. You can get the body with response.readEntity(String.class). This way you don't need to handle any exceptions. Just handle conditions based on the status.
Do note though that the Response above is an inbound Response, which is different from the outbound Response in your current code. So just make sure not to try and send out an inbound Response.
Also see this answer and it's comments for some design ideas.
I write a rest ful web service that has formParam and return a list . and I test it in postman . but I get this error.HTTP Status 500.The server encountered an internal error that prevented it from fulfilling this request error.
here is my service :
#Path("/report")
public class weightingResource {
#POST
#Path("/loadWeightingByPlate")
//#Produces(MediaType.APPLICATION_XML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public List<Weighting> LoadWeightingInSpecTimeInSpecPlate(
#FormParam("plate") String plate,
#FormParam("startTime") String _startTime,
#FormParam("endTime") String _endTime,
#Context HttpServletRequest req) {
Long startTime = new Long(_startTime);
Long endTime = new Long(_endTime);
try {
List<Weighting> weightings = Weighting.LoadWeightingInSpecTimeInSpecPlate(startTime, endTime, plate);
System.out.println("no error");
return weightings;
} catch (Exception ex) {
System.out.println("Exception = " + ex);
return null;
}
}
}
I test Weighting.LoadWeightingInSpecTimeInSpecPlate(startTime, endTime, plate) and this work correctly . can eny one help me ?
stack trace :
Blockquote21-Aug-2015 17:44:31.133 WARNING [http-nio-8084-exec-197] org.glassfish.jersey.servlet.WebComponent.filterFormParameters A servlet request to the URI http://127.0.0.1:8084/fsc-access/rest/report/loadWeightingByPlate contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
21-Aug-2015 17:44:31.210 SEVERE [http-nio-8084-exec-197] org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo MessageBodyWriter not found for media type=application/xml, type=class java.util.ArrayList, genericType=java.util.List.
now my service works well and
I write a client to use this service but I get an error : HTTP 400 Bad Request :javax.ws.rs.BadRequestException
String webserviceURI = "http://localhost:8084/fsc-access";
ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
URI serviceURI = UriBuilder.fromUri(webserviceURI).build();
WebTarget webTarget = client.target(serviceURI);
MultivaluedMap formData = new MultivaluedMapImpl();
formData.add("plate", plate);
formData.add("startTime", start.toString());
formData.add("endTime", end.toString());
Weightings weightings = new Weightings();
weightings.getWeightings().addAll((Collection<? extends Weighting>) webTarget.path("rest").path("report").path("loadWeightingByPlate").
request().accept(MediaType.APPLICATION_XML).post(javax.ws.rs.client.Entity.form(formData), Weightings.class));
how I can fix it ?
First, the reason you're getting the 500 is told by the following message:
21-Aug-2015 17:44:31.210 SEVERE [http-nio-8084-exec-197]
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo
MessageBodyWriter not found for media type=application/xml, type=class
java.util.ArrayList, genericType=java.util.List.
That indicates that Jersey, your servlet, does not have a registered MessageBodyWriter that returns true from isWritable() with the following parameters
media type=application/xml,
type=class java.util.ArrayList,
genericType=java.util.List
AFAIK Jersey usually has access to a JAXB implementation, but some JAXB implementations cannot handle generics like List correctly. What I would suggest is that you create a new class called Weightings that is a list wrapper for a collection of Weighting objects. This is a common thing to do with JAXB models.
#XmlRootElement
public class Weightings {
#XmlElement
private final List<Weighting> weightings = new ArrayList<Weighting>();
public List<Weighting> getWeightings() {
return weightings;
}
}
Then modify your resource to return the new type:
#POST
#Path("/loadWeightingByPlate")
#Produces(MediaType.APPLICATION_XML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Weightings LoadWeightingInSpecTimeInSpecPlate(
#FormParam("plate") String plate,
#FormParam("startTime") String _startTime,
#FormParam("endTime") String _endTime,
#Context HttpServletRequest req) {
Long startTime = new Long(_startTime);
Long endTime = new Long(_endTime);
try {
Weightings weightings = new Weightings();
weightings.getWeightings().addAll( Weighting.LoadWeightingInSpecTimeInSpecPlate(startTime, endTime, plate));
System.out.println("no error");
return weightings;
} catch (Exception ex) {
System.out.println("Exception = " + ex);
return null;
}
}
That should fix your 500 response. Your other option is to look around and test other JAXB or application/xml MessageBodyWriter implementations that might support generics better.
The other warning you're getting:
21-Aug-2015 17:44:31.133 WARNING [http-nio-8084-exec-197]
org.glassfish.jersey.servlet.WebComponent.filterFormParameters A
servlet request to the URI
http://127.0.0.1:8084/fsc-access/rest/report/loadWeightingByPlate
contains form parameters in the request body but the request body has
been consumed by the servlet or a servlet filter accessing the request
parameters. Only resource methods using #FormParam will work as
expected. Resource methods consuming the request body by other means
will not work as expected.
I would take a look at this question if you're interested in fixing the warning.
I wrote the following code to implement a Java web service that communicates with an application written in another language on the same host:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
#WebService(name = "MyWebService")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class MyWebService {
#WebMethod(operationName = "methodName", action = "urn:#methodName")
#WebResult(name = "result", partName = "output")
public String methodName(#WebParam(name = "param1", partName = "input") String param1,
#WebParam(name = "param2", partName = "input") String param2){
// ...do something
return "You called this service with params: " + param1 + "," + param2;
}
Since requirements are not to use an application server to expose the web service I instantiated the service from another class as follows:
Endpoint endpoint = Endpoint.create(new MyWebService());
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
Questions:
1) Which is the simplest way to secure this service with username and password considering the architecture of this project?
Any code sample would be greatly appreciated.
2) I made some research and found the use of SOAPHandler and I think it would work for me.
In the case of using the SOAPHandler class how do I add headers to the message to require authentication from the client?
Thank you in advance
thanks so much for the response that's the direction I'm following too but
when I check any of the headers for example:
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
Iterator<SOAPElement> iterator = header.getAllAttributes();
I get a nullpointer exception...any ideas?
I did a working program. Just to add to what you already found out, following is a way to use handler
Endpoint endpoint = Endpoint.create(new MyWebService());
Binding binding = endpoint.getBinding();
List<Handler> handlerChain = new ArrayList<Handler>(1);
handlerChain.add(new MyHandler());
binding.setHandlerChain(handlerChain);
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
MyHandler is class extending Handler interface. Alternately, you can use #HandlerChain annotation which will need an xml configuration file for handlers. Configure this for incoming messages only
public class MyHandler implements SOAPHandler{
#Override
public Set<?> getHeaders() {
// TODO Auto-generated method stub
return null;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public boolean handleFault(MessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean handleMessage(MessageContext context) {
System.out.println("Hehehe the handler");
SOAPMessageContext soapContext = (SOAPMessageContext)context;
try {
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
//Check there if the required data (username/password) is present in header or not and return true/false accordingly.
} catch (SOAPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
}
From the client side also, if your client is using JAB-WS, you will have to use client handlers. Following is a typical JAX-WS client invocation example
Dispatch<Source> dispatch = … create a Dispatch<Source>
dispatch.getBinding().setHandlerChain(chain)
Source request = … create a Source object
Source response = dispatch.invoke(request);
Here the handler in chain will add header to outgoing request. Configure this for Outgoing messages only.
What you did is fair enough.
Concerning the authentication you can just expose a method for passing user name and password as login credentials.
Once the user has provided the correct credentials the user has been authenticated.
Note: Now you must maintain session data and make sure that an incoming request is from an authenticated user. The Endpoint just deploys internally a lightweight http server. You must design you web service implementation to keep "state" among requests.
You have 2 more options.
Do the authentication at the SOAP level. I would not really recomend
it. But if you do, note that the Endpoint does not deploy a
WSDL. So you must communicate exactly to the client connecting,
the SOAP header you expect. It is possible though to write a WSDL by
yourself and "attach" it to the Endpoint.
Do the authentication at the http request level. I.e. add a token or
cookie to the http request. To be honest I do not remember if this
is easy using the Endpoint