Spring Cloud Feign not processing #RequestMapping UriTemplate - spring-cloud

I have an existing Spring Cloud Feign client interface that has many mappings for my server-side API. I'm adding some new methods, and I'm suddenly running into an error. I'm trying to add a method of the form:
#RequestMapping(value = "/tasks/{id}", method = GET)
public Resource<Task> getTask(#PathVariable("id")Long id);
Everything compiles fine, but when I try to make a call to the getTask() method above, I always get a an IllegalArgumentException complaining about the URL not being valid. Which is true, because the URL still contains the UriTemplate {id}.
The full stack is:
java.lang.IllegalArgumentException: Illegal character in path at index 29: http://connect/connect/tasks/{id}
at java.net.URI$Parser.fail(URI.java:2848)
at java.net.URI$Parser.checkChars(URI.java:3021)
at java.net.URI$Parser.parseHierarchical(URI.java:3105)
at java.net.URI$Parser.parse(URI.java:3053)
at java.net.URI.<init>(URI.java:588)
at java.net.URI.create(URI.java:850)
at feign.ribbon.RibbonClient.execute(RibbonClient.java:64)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:92)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:71)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:94)
at com.sun.proxy.$Proxy55.getTask(Unknown Source)
There are dozens of other methods in the same interface that use this exact same pattern, and everything runs fine. I cannot for the life of me figure out why Feign/Spring is suddenly having an issue with this method. I've tried every possible combination of settings and ways to write the method. If I simply remove the {id}, the call will go through, but obviously returns the wrong data, since its missing the id portion of the URI.
I'm using Spring Cloud Angel.SR6 with Spring Boot 1.2.8 and Feign 8.5.0.

I resolve my issue. It turns out the error message was quite misleading. I turns out the method was being passed in a Null value, so there was nothing for the URI template to replace. Since its an interface, I cannot add logic to assert the Not Null requirement, at least as far as I know right now.
Once I figured that out and resolved it upstream of the call, the IllegalArgumentException was eliminated. Notice that no where is the fact that the input was NULL noted in the stack trace in my original note.

Related

Microprofile Config: Dynamic ConfigSource values for faulttolerance metric tags

i'm currently working on a solution to setting metric tags for the microprofile fault-tolerance framework. We're using it together with metrics, but one cannot directly set tags via the fault-tolerance annotations.
So we came up with a workaround setting a ThreadLocal value via an Interceptor, which then is read by a custom ConfigSource. The ConfigSource checks for "mp.metrics.tags" and "MP_METRICS_TAGS" config keys in it's getValue(final String propertyName) method. This would basically work if the getValue would get called every time a fault-tolerance annotation is processed. But is seems like this is not the case and the invocations of the method happen randomly.
In my oppinion ConfigSources and their getValue(final String propertyName) should always get called as a developer might rely on config values which could change every second.
Any ideas why the config source is not called?
It looks like the custom ConfigSource does no longer get called when returning null multiple times or at least during the server startup phase. In the mentioned scenario this can be bypassed by returning an empty string. Then the ConfigSource also gets called for every getValue() method call at runtime.
The MicroProfile Config 1.4 specification indicates that no caching of a ConfigSource's value should occur, so if your MicroProfile Config Config implementation (you don't say which implementation it is) is caching the results of a call to ConfigSource#getValue() it is not compliant, as best as I can tell (the specification is flawed, not very rigorous, and its TCK is spotty but it certainly seems to be pretty clear on this issue).
(Do note that a given ConfigSource implementation may, of course, decide to return cached values from its getValue() method.)

CXF - webservice endpoint has changed, WSDL has not

My application used CXF WSDL2JAVA to generate the Java classes from a client supplied WSDL.
They've now changed their webservice to have 3 additional string fields in the response.
It would be a bit of rework for us to regenerate the java classes, but we're getting an exception:
javax.xml.ws.soap.SOAPFaultException: Unmarshalling Error: unexpected element (newElement)
Is there a way to get CXF to ignore the extra elements in the response?
If you add an endpoint property of:
"set-jaxb-validation-event-handler" to "false"
then CXF will not add an event handler into the JAXB unmarshaller and the unexpected elements are silently ignored. You'll need to make sure the rest of the message is exactly correct though as you will not get any errors or anything if the message isn't correct. For example, the common thing to happen is someone sends qualified elements when JAXB is expecting unqualified elements. Without setting the handler, all the fields in the object would end up as "null". With the handler, you would get a message like:
unexpected element (uri:"http://my.namespace.com", local:"myField"). Expected elements are <{}myField>
so you can see that the namespace qualification is the reason.

Http DELETE with parameters using Jersey

I have code that contains several different DELETE methods, one of which takes a parameter, or at least ideally it would take a parameter. However when I make this request, either through CURL or through a web client, it doesn't work. The other DELETE requests function fine and I've hard-coded in the parameter that I want to pass just to see if the call works and it does. I've tried this with both PathParam and QueryParam and neither works. Here's how I'm using the PathParams, which I'm pretty sure is correct, QueryParams looks very similar so I don't want to post that too.
#DELETE
#Path("/byId/{id}")
public void deleteById(#PathParam("id") String id)
And then essentially the same thing for QueryParams but obviously the path is different
From what I understand a lot of RESTful APIs ignore any kind of request body with a DELETE request, or treat it as a PUT or POST. Is there any way around this? Basically I have a database that contains several objects and I need to delete one based on a unique identifier, to be passed in by the client. If there is no way around this is there some other way that I could do it?
Its possible that I'm missing something obvious here as I've only been using Jersey for a few weeks now and up to this point had never even heard of the concept of RESTful services.
You can send information to a RESTful service as either headers, path param, query param or message body.
If all the values go through as expected there is no problem with jax-rs/jersey. Now you need to debug the code and fix your implementation. jax-rs will only match a DELETE call with the DELETE http method you have implemented.
It is then your responsibility to actually perform a DELETE operation of the resource within the method. jax-rs or jersey is not going to complain if you do not DELETE or if you do some other action within the method.

Understanding WSDL's, SOAP, REST, etc

I'm trying to learn how to use WSDL's to call web services from a Grails project. I've been provided with the WSDL and some XML results for reference.
I've been able to generate Java code from the WSDL, and everything seems to be working correctly.
Here's the WSDL: http://www.restfulwebservices.net/rest/USAZipCodeService.svc?wsdl
And here is the XML: http://api.geonames.org/postalCodeSearch?placename=MN&username=demo
I am receiving this exception in my project:
ERROR client.WebServiceClientFactoryImpl$WSClientInvocationHandler - No namespace on "geonames" element.
javax.xml.ws.soap.SOAPFaultException: No namespace on "geonames" element.
It seems like it is saying that the XML returned isn't valid for SOAP? Am I missing/misunderstanding some pieces the puzzle here? It is all pretty new to me.
Edit:
I am trying to use a Grails plugin called cxf client: https://github.com/ctoestreich/cxf-client
It is configured with the following in Config.groovy (something could be wrong/missing here?):
wsdl = "http://www.restfulwebservices.net/wcf/USAZipCodeService.svc?wsdl"
namespace = "cxf.client.postalcode"
clientInterface = "cxf.client.postalcode.IPostalCodeService"
serviceEndpointAddress = "http://api.geonames.org/postalCodeSearch"
I guess you just sent the XML returned from http://api.geonames.org/postalCodeSearch?placename=MN&username=demo as a parameter to the web service. Obviously, from the WSDL description returned you can see there is no such element named geonames, so the SOAPFaultException exception is quite a fair result.
To fix it, you have to refer to the WSDL description carefully, to make sure the invoke method has the right parameters work with whatever defined in the USAZipCodeService WSDL description tags like <wsdl:operation> and <wsdl:message>.
Another issue: 2 different WSDLs were metioned in your invoker and Config.groovy. The former is a RESTful service, and the later is a SOAP one. They work with different invoke methods and parameters, so make sure your code has consistent invoker and parameters, too.

JAX-RS and unknown query parameters

I have a Java client that calls a RESTEasy (JAX-RS) Java server. It is possible that some of my users may have a newer version of the client than the server.
That client may call a resource on the server that contains query parameters that the server does not know about. Is it possible to detect this on the server side and return an error?
I understand that if the client calls a URL that has not been implemented yet on the server, the client will get a 404 error, but what happens if the client passes in a query parameter that is not implemented (e.g.: ?sort_by=last_name)?
Is it possible to detect this on the server side and return an error?
Yes, you can do it. I think the easiest way is to use #Context UriInfo. You can obtain all query parameters by calling getQueryParameters() method. So you know if there are any unknown parameters and you can return error.
but what happens if the client passes in a query parameter that is not implemented
If you implement no special support of handling "unknown" parameters, the resource will be called and the parameter will be silently ignored.
Personally I think that it's better to ignore the unknown parameters. If you just ignore them, it may help to make the API backward compatible.
You should definitely check out the JAX-RS filters (org.apache.cxf.jaxrs.ext.RequestHandler) to intercept, validate, manipulate request, e.g. for security or validatng query parameters.
If you declared all your parameters using annotations you can parse the web.xml file for the resource class names (see possible regex below) and use the full qualified class names to access the declared annotations for methods (like javax.ws.rs.GET) and method parameters (like javax.ws.rs.QueryParam) to scan all available web service resources - this way you don't have to manually add all resource classes to your filter.
Store this information in static variables so you just have to parse this stuff the first time you hit your filter.
In your filter you can access the org.apache.cxf.message.Message for the incoming request. The query string is easy to access - if you also want to validate form parameters and multipart names, you have to reas the message content and write it back to the message (this gets a bit nasty since you have to deal with multipart boundaries etc).
To 'index' the resources I just take the HTTP method and append the path (which is then used as key to access the declared parameters.
You can use the ServletContext to read the web.xml file. For extracting the resource classes this regex might be helpful
String webxml = readInputStreamAsString(context.getResourceAsStream("WEB-INF/web.xml"));
Pattern serviceClassesPattern = Pattern.compile("<param-name>jaxrs.serviceClasses</param-name>.*?<param-value>(.*?)</param-value>", Pattern.DOTALL | Pattern.MULTILINE);