I'm currently developing a REST Application with Apache Camel (using camel-spring), and getting some confusing behaviour. I have a set of endpoints defined in REST DSL, some are simply proxy requests to another server, and some others are passed on to routes I've defined for data aggregation. The DSL looks as follows:
<rest>
<!-- Specific DSL requests -->
<get uri="/v1/aggregate/data" consumes="application/json" produces="application/json">
<to uri="direct:dataEnrichment" />
</get>
<get uri="/v1/customer/{custId}/devices" consumes="application/json" produces="application/json">
<to uri="direct:getCustomerDevices" />
</get>
<get uri="/v1/login/{custId}" consumes="application/json" produces="application/json">
<to uri="direct:getCustomer" />
</get>
<get uri="/v1/status" consumes="application/json" produces="application/json">
<to uri="direct:statusInfo" />
</get>
<!-- Proxy requests -->
<post uri="/v1?matchOnUriPrefix=true&chunked=false">
<to uri="direct:proxyOut" />
</post>
<get uri="/v1?matchOnUriPrefix=true&chunked=false">
<to uri="direct:proxyOut" />
</get>
<put uri="/v1?matchOnUriPrefix=true&chunked=false">
<to uri="direct:proxyOut" />
</put>
<delete uri="/v1?matchOnUriPrefix=true&chunked=false">
<to uri="direct:proxyOut" />
</delete>
</rest>
The idea is that any requests with no exact matching URI get proxied through to another system (which is not Apache Camel). This saves me having to write a definition for every REST API on the other system (there are a lot).
All was working well, until I added the two requests with {custId} in the URI. Those requests work fine, but every time I try a URI that should get proxied through, I get 405 Method Not Allowed.
EDIT:
I should also have mentioned that I'm using Jetty as the REST component. Camel is running stand-alone, using org.apache.camel.spring.Main to start it up. I'm calling it with Postman at this stage, and the 405 response appears to be coming from Jetty/Camel.
The REST configuration looks like this (The security handler is using the Jetty BasicAuthenticator):
<restConfiguration contextPath="/mobilegateway/api" scheme="http"
host="0.0.0.0" bindingMode="off"
enableCORS="true"
component="jetty" port="8079">
<endpointProperty key="handlers" value="securityHandler" />
<endpointProperty key="sessionSupport" value="true" />
<endpointProperty key="httpClient.idleTimeout" value="30000" />
</restConfiguration>
The Proxy requests all send to the direct:proxyOut route, which looks like this:
<route id="proxyOutbound">
<description>A simple outbound route to proxy REST requests.</description>
<from uri="direct:proxyOut" />
<removeHeaders pattern="Authorization" />
<to
uri="http://{{remoteAddress}}/data/json?bridgeEndpoint=true&throwExceptionOnFailure=false" />
</route>
The intention is that everything after the /v1 in the URI is passed over in the proxied request. I've checked using Wireshark, and the request is not getting proxied. If I remove the routes with {custId} in the path, everything works fine.
Am I doing something wrong or is this a bug in camel/camel-spring?
Camel bug, the details of which can be found here:
https://issues.apache.org/jira/browse/CAMEL-11951
Its not clear whom is returning 405, is it the proxied backend you call, or does it not call that backend at all.
However when you proxy HTTP via Camel, then you may need to remove some CamelHttp* headers that can interfere.
So try adding
<removeHeaders pattern="CamelHttp*" />
The similar bug is still in place, I've filed one here:
https://issues.apache.org/jira/browse/CAMEL-15363
I've managed to make a workaround for my case, see my comment in the bugtracker
Related
I have a spring project using apache camel.
I want to try the services provided in camel-context.xml using a postman request.
How can I infer the path variables and endpoint?
The method I want to use is specified like below on camel-context.xml and I have parameters providing the contains conditions.
<route>
<from uri="netty:udp://{{camel.netty.server}}:{{camel.netty.udp.port}}?sync=false&allowDefaultCodec=true;" />
<convertBodyTo type="java.lang.String" charset="ISO-8859-9" />
<choice>
<when>
<simple trim="true">${bodyAs(String)} contains '"ISN":"90"'</simple>
<bean ref="Feaser" method="run" cache="false"></bean>
<to uri="mock:result" />
</when>
...
Postman doesn't support sending on UDP.
I have configured an XML camel route as mentioned below
<route>
<from uri="sftp://testuser#localhost?password=test&delete=true" />
<setHeader name="CamelAwsS3Key">
<simple>${in.header.camelFileName}</simple>
</setHeader>
<to uri="aws-s3://myTestBucket?accessKey=******&secretKey=RAW(******)&deleteAfterWrite=false®ion=AP_SOUTH_1" />
</route>
I ahve been trying to create test cases for the same. contextLoads() tries to access the s3 localtion in real time which i want to prevent.
Saw there are solutions with LocalStack, is there any other way i can test a scenario without other libraries?
I am using apache camel to create routes between endpoints where through one URI (API Gateway) running on Tomcat on one port, I am mapping to another URI running on Tomcat on different domain and port.
<bean id="hostnameVerifier" class="org.apache.http.conn.ssl.AllowAllHostnameVerifier" />
...
<camel:sslContextParameters id="ssl">
<camel:keyManagers keyPassword="password">
<camel:keyStore ... />
</camel:keyManagers>
<camel:trustManagers>
<camel:keyStore ... />
</camel:trustManagers>
</camel:sslContextParameters>
....
<rest path="/MyService" consumes="application/json" produces="application/json">
<post uri="/login">
<description>Authenticate User</description>
<route streamCache="true">
<to
uri="https4://domain-b:9000/Auth/user/login?bridgeEndpoint=true&sslContextParametersRef=ssl&x509HostnameVerifier=hostnameVerifier" />
</route>
</post>
...
</rest>
Now as far as I am hardcoding the domain-b in my to endpoints, things are working fine. Problem comes when I have to dynamically fill that value from an input from some configuration file.
This is how I am trying to achieve the same -
<bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
<property name="location" value="classpath:${LOCATION_PATH}propsfile.properties"/>
</bean>
The name of the properties key is "domain", now in my end points defintion I am writing the same as -
<to
uri="https4://${properties.domain}:9000/Auth/user/login?bridgeEndpoint=true&sslContextParametersRef=ssl&x509HostnameVerifier=hostnameVerifier" />
Basically after loading the properties in a bean named properties, I am trying to replace the domain-b with ${properties.domain} or #{properties.domain}, but does not seem to be working.
If anyone can suggest, in XML based config only, how can I read the URL domain from the properties file, that will be really awesome.
-AJ
You have to use property place holder to achieve dynamic uri the way you want.
For example:
<camelContext ...>
<propertyPlaceholder id="properties" location="YOUR_PROPERTY_FILE_LOCATION"/>
</camelContext>
And then try with
<to uri="https4://{{properties.domain}}:9000/.......>
Note: When you are configuring your property file using spring bean "PropertiesComponent", you have to use camel Property component inside your camel route to achieve dynamic value loading.
Since Camel 2.16
We can use
.from("file://path")
.toD("file://folder/${file:onlyname}")
We can use file
I'm trying to create an application that gets information from a browser and drops it in a queue. That data is then picked up from the queue and sent through an application for security. The security app should drop it in a different queue when it is done to be picked up by a separate action application.
Could anyone help me along with the routing? Basically, the route I'm looking for is:
Browser/UI -> Qnonsecure -> security app -> QSecure -> action app
What I understand now is the following:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="jms:queue:QnonSecure"/>
<to uri="jms:queue:QSecure"/>
</route>
</camelContext>
How Can I change this to route to and from applications.
How do I send the input from the browser into QnonSecure? Also, where in my code do I call the security app between the QnonSecure and QSecure?
There is more than one possible solution. Take the following route as a starting point:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="restlet:http://localhost:8081/myApp?restletMethod=post"/>
<to uri="jms:queue:QnonSecure" pattern="InOut" />
<enrich uri="direct:securityApp"/>
<choice>
<when>
<simple>${header.myHeader} == "SECURE"</simple>
<to uri="jms:queue:QSecure" pattern="InOut" />
<to uri="direct:actionApp" />
</when>
<otherwise>
<!-- handle non valid requests -->
</otherwise>
</choice>
</route>
</camelContext>
Steps:
The browser sends a POST request to the Camel restlet component. This could be done via JavaScript, a link and/or just a ordinary submit button.
The body is sent to jms:queue:QnonSecure. As we use the InOut pattern, this is done in a synchronous manner and the response is fetched.
The response of jms:queue:QnonSecure is sent to direct:securityApp where the credentials are tested. If they are correct, the header myHeader is set to SECURE (or any other value).
In the choice statement, myHeader is tested. In the secure case, jms:queue:QSecure and finally direct:actionApp is invoked.
I'm using Camel 2.10.2 and I have the following configuration:
<camelContext id="camelContext" trace="true" xmlns="http://camel.apache.org/schema/spring">
<errorHandler type="LoggingErrorHandler" level="INFO" logName="my.logger" id="webserviceLoggingHandler"/>
<route id="webService" errorHandlerRef="webserviceLoggingHandler">
<from uri="direct:webService" />
<to uri="spring-ws:{{webservice.url}}?messageFactory=#messageFactory&messageSender=#messageSender" />
<onException>
<exception>java.lang.Exception</exception>
<to uri="log:my.logger?level=INFO"/>
</onException>
</route>
<route id="validate">
<from uri="activemq:validate" />
<to uri="direct:webService" />
</route>
</camelContext>
I would expect this to log to the console. Instead what I'm finding is that exceptions (for example IOExceptions like certificate errors) are just being consumed and I really need them to be logged.
Debugging the code I can see that the SpringDefaultErrorHandler is being used and this is delegating to the LoggingExceptionHandler which has an injected CamelLogger which has a NOPLogger injected. This means that nothing is being logged at all.
After reading through the docs I am unsure if I need to implement a specific error handler and relevant onException handling, or if I should instead just use the log component, or a mix of the two as I have above?
Any guidance gratefully received.
many Thanks
OK, I found the problem. My camel setup was fine. We recently upgraded from Camel 2.6.0 to 2.10.2. Reading the camel docs I found that from camel 2.7 slf4j was used for logging instead of commons-logging. I already had the log4j and slf4j jars but was missing the slf4j-log4j binding jar. This is what was causing all logging to go to an NOPLogger. Once I dropped the jar in it all works fine.
Thanks