Apache Camel XSLT endpoint options with http resource - java

Camel's XSLT component has a number of configurable options. According to documentation, these options are added to the URI in the form of
?option=value&option=value&...
However, if the resource is coming over HTTP, all options are used as a query string for the HTTP request and then dropped. From XsltComponent.java:
// if its a http uri, then append additional parameters as they are part of the uri
if (ResourceHelper.isHttpUri(resourceUri)) {
resourceUri = ResourceHelper.appendParameters(resourceUri, parameters);
}
and ResourceHelper.appendParameters():
if (!parameters.isEmpty()) {
String query = URISupport.createQueryString(parameters);
URI u = new URI(uri);
u = URISupport.createURIWithQuery(u, query);
parameters.clear();
return u.toString();
}
The comment in XsltComponent makes me think this is functioning as designed, but it seems like it should be a pretty common scenario. Has anyone found a way around this? I don't want to copy the resources locally, as they are subject to change.
For reference, I'm using the Java DSL inside a Spring Boot application.

No not all options, only any additional options that are not an option on the xslt component, for example if you have ....?foo=bar then because foo is not an option on the xslt component it is used in the http url, as you may need to provide a few options there, to be able to access the resource.

Related

ContentCachingRequestWrapper only captures POST request with Content-Type:application/x-www-form-urlencoded

I am trying to intercept all incoming HTTP requests and process the body attached to these requests in my Spring MVC (not Spring Boot) app. To implement this "inbound-interceptor", I am using Spring's HandlerInterceptor interface. Once the request is intercepted, I am trying to retrieve the body as follows:
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
Map<String, String[]> params = requestWrapper.getParameterMap();
byte[] body = requestWrapper.getContentAsByteArray();
Referring to this article, the limitations of trying to extract the body this way are:
Content-type of the request must be x-www-form-urlencoded
Method-type must be POST
For the application I am building, I cannot enforce either of these constraints as the calls come from heterogeneous sources beyond my control. Is there some way to override this behavior to allow extraction of the body for requests not supported by default? Or, alternatively, is there another approach to performing this task?
P.S. I am performing logging + some custom processing on the body. So solutions such as the ones mentioned in this answer are not too helpful
Have you tried Logbook? https://github.com/zalando/logbook Works with pure Spring.
Their Default Log Writer looks promising: https://github.com/zalando/logbook/blob/main/logbook-core/src/main/java/org/zalando/logbook/DefaultHttpLogWriter.java
And you may just want to extend this class to log to all Loggers you want.
You can even do something completely different with the request besides logging.

How to add matchOnUriPrefix on existing Camel Jetty REST routes?

We have existing REST routes working with Camel 2.23.1 and jetty. We redirect incoming calls to an appropriate server based on the uri, query, and the user's authentication. We want to handle this more generally.
How can we modify the following code to handle any uri with "/say" as the prefix?
In our RouteBuilder:
RestConfigurationDefinition rConfig = restConfiguration()
.component("jetty")
.port(webserverPort)
.contextPath("/")
.bindingMode(RestBindingMode.off)
.enableCORS(true)
.dataFormatProperty("prettyPrint", "true");
rest("/say")
.get().to("direct:test");
from("direct:test")
.bean(RouteRest.class, "getTestURI(*,*)")
.to("mock:output");
We have tried adding a property to the restConfiguration, ala
.componentProperty("matchOnUriPrefix", "true");
We have tried adding the same property to the rest route definition, ala
rest("/bye?matchOnUriPrefix=true")
We have tried creating a new from statement, which seems to break everything, ala
from("jetty://0.0.0.0:8123/now?matchOnUriPrefix=true").to("direct:test");
I am aware of this question and answer, but don't know how to apply it to my case:
stackoverflow.com/questions/39341784
Further, is it possible to match some incoming calls with explicitly defined uri's, like "/admin/status", and all other uri's to "direct:test"?
We ended up taking out the restConfiguration() entirely and configuring endpoints individually, which fit our expanding requirements anyway. Our oritinal restConfiguration() was limiting the messages that could get to the endpoints themselves. Perhaps we could have modified the restConfiguration directly to enable greater flexibility, including removal of .contextPath("/"). This directly allowed the following code to work:
from("jetty:http://{{ip}}:{{port}}?matchOnUriPrefix=true")
.bean(RestForward.class, "checkUserAuth(*)")
.bean(RestForward.class, "checkDevice(*)")
.bean(RestForward.class, "forward(*,*)")
.to("mock:output");

Accessing Parameters mapped by #RequestMapping for Tracing

I am currently working on a monitoring application using Spring Cloud Sleuth. Currently I try to collect as much information about my requests as possible.
To keep the approach as scalable as possible I use GenericFilterBeans and HandlerInterceptorAdapter to access information from the requests sent to the REST-API.
I am struggling with getting parameters of a REST-call where the parameters are mapped from the URL like in the following code snippet:
#RequestMapping(
value = {"/{service}/{route_id}/book", "/accounting-core-service/{service}/{route_id}/book"},
method = RequestMethod.GET)
#ResponseBody
public ModelAndView book(#PathVariable(value="service") String serviceName,
#PathVariable(value = "route_id") int routeId,
HttpServletResponse response) {
/*Do something*/
}
The question is not, whether it is good practice or not to write it like so. The question is whether there is an approach similar to Filter or Interceptor (or the proper use of them) to access those parameters.
A requirement is, that it can be applied easily to an application by adding very few lines of code. Annotating every Method call manually or manually inserting the code to write the parameters into the trace from within the method is not feasible for me.
If you need more information feel free to ask. I will provide you with all information you need to help me with my problem.
Although not officially supported (as it's not written in the reference documentation), Spring MVC holds that information as request attributes.
You could create your own HandlerInterceptor, ordered right after the Sleuth one, and get that information from the request like this:
// "/{service}/{route_id}/book"
String matchingPattern = (String) request
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// "service" => "fooService", "route_id" => "42"
Map<String, String> templateVariables = (Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Note, the template variables are already decoded at that point, which is probably what you want anyway.
You can create a Filter that intercept all the requests.
For each request you can retrieve also this informations:
query parameters
body of request
url
header parameters
You can save all this data as you need.
This is the best way you can do that.
If you need to group all urls like /{service}/{route_id}/book in a "family" of urls you can do that splitting the url and check if it is part of the family, but when a new family is added in your code you need to update the filter (or configure something in an external file or database).

HTTPS to file in Apache Camel

I need to access a HTTPS Resource which is using Basic Authentication and It is a GET Resource. I have written a code in java using URIBuilder and adding the username, password etc as Headers to the URIBuilder and using Apache HTTPClient to access the resource and it is working well.
Now my requirement is to implement the same functionality using Apache Camel, Well I tried using Camel HTTP component and by using Java DSL.
The problem is I am just able to provide the URI. But how can I add various Headers to this URI ?.
I suggest you use the http4 component as a way to consume this secure resource. From reading the docs of the component you can see it's possible to set the query parameters, path and even uri at runtime.
In answer to your specific question, the headers on the exchange at the point it reaches the .to() will be sent as headers in the HTTP request so you may want to define a header filter strategy. It has support for http basic auth and you can set your credentials via the authUsername and authPassword headers. You may need to provide a custom HttpContext because you're authenticating via https as it suggests at the bottom of the docs. For example:
from("direct:in")
.process(new Processor() {
public void process(Exchange exchange) {
//These headers you set here will get sent with the http request in the to() after this processor
exchange.getIn().setHeader("authUsername", "username");
exchange.getIn().setHeader("authPassword", "password");
}
})
.to("https4://uri.com);
Use the simple language to add headers if you are using blueprint or the java dsl if its pure java. Simple example:
from("direct:start")
.setHeader(Exchange.HTTP_METHOD, constant(org.apache.camel.component.http4.HttpMethods.POST))
.to("http4://www.google.com")
.to("mock:results");

Do "pure Spring" based servlets have Endpoint URL like CXF based WSDL have?

I have a perfectly working demo server/client apps pair using Spring (only!) -- no CXF or WSDL involved. It runs with the help of Apache Tomcat 7.0.34.
I was curious to see whether I can see any trace to its presence on a browser (http://localhost:8080/) but I couldn't find any hint to a URL in the source code (copied verbatim from a tutorial).
I then found this thread which provided a way to get the endpoint's URL:
TransportContext tc = TransportContextHolder.getTransportContext();
WebServiceConnection wc = tc.getConnection();
URI uri = wc.getUri();
I added this to my demo/tutorial client's code and while the first statement (getTransportContext()) doesn't throw any exception, it returns null and so the second one (getConnection()) throws a NullPointerException .
Why?
Do "pure" Spring based servlets lack endpoint URLs?
If not, what am I missing? What is the way to retrieve the service's URL?
I know that the client knows about the server by means of the context path and the bean id (as defined in the shared beans.xml):
ClassPathXmlApplicationContext appContext =
new ClassPathXmlApplicationContext( new String[] {"/sample/spring/beans.xml" } );
But isn't there a URL equivalent to this? Like WSDLs do?
I agree with #GreyBeardedGeek's comment, there's no concept of endpoint URL on Spring web-app (java servlet web-app in general).
If you do need to runtime lookup what URL the user used to reach your app, you can use ServletRequest / HttpServletRequest methods such as getRemoteAddr(), getRemoteHost(), getURL(), getContextPath() etc. Eg:
#RequestMapping("/home")
public String home(HttpServletRequest req) {
String host = req.getRemoteHost();
// ...
}
However keep in mind multiple URL can point to the same tomcat server, eg if a reverse proxy / DNS CName is setup. And that might (or not) yield different URL for you.

Categories

Resources