Modify the handled exception fault message in Apache camel - exception

In my camel route, I am making a direct-vm call to an existing bundle. In that service, they have handled the exception and setting up some custom fault message. When any exception occurs in that service they are sending fault message like below.
{
"errorCode": "400",
"errorMessage": "Unknown error"
}
But I need to form my own custom fault message based on the fault message received. But once an exception occurs in second bundle, i am not able to recive the fault message and modify it. below is how my route looks.
<route handleFault="true" streamCache="true" id="route1">
<from uri="cxfrs://bean://testCxf?synchronous=true"/>
<log message="The consumer message ${body}"/>
<bean ref="requestValidator" method="validateRequest"/>
<to uri="direct-vm:retrieveData"/>
<bean ref="validateResponse" method="validate"/>//need to manipulate the fault message coming from bundle 2 in this bean.
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean ref="faultMapper" method="mapFault"/>
</onException>
</route>
Below is the existing direct:vm route.
<route handleFault="true" streamCache="true" id="route2">
<from uri="direct-vm:retrieveData"/>
<bean ref="manipulateData" method"manipulate"/>
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean ref="faultMapper1" method="mapFault1"/>
</onException>
</route>
i need to intercept the fault that is mapped in the class faultmapper1, in my route1 after the direct-vm call.How to acheive this?. I will not be allowed to change anything in the existing 2nd bundle. Thanks in advance.

After some research found that this is not possible. To achieve the requirement, I have made the handled constant as false in the second route and added a , block in the first route. So that I can catch the exception thrown and handled it with some custom fault mapping.

Related

exception handling in apache servicemix

I am new to ServiceMix am trying on exception handling in ServiceMix.
when I try to print the exception, it also contains the body of the request in it. Is there any way that I can extract only the errors from exception?
<from uri="activemq:topic://topic1"/>
<!-- Schema validationm for the request received from Biblio -->
<doTry>
<to uri="validator:http://localhost/employee.xsd"/>
<doCatch>
<exception>java.lang.Exception</exception>
<log message="${exception.message}"/>
</doCatch>
</doTry>
below is the logged exception:
org.apache.xerces.jaxp.validation.SimpleXMLSchema#a0059b5
errors: [
org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: 'asd' is not a valid value for 'integer'., Line : -1, Column : -1
org.xml.sax.SAXParseException: cvc-type.3.1.3: The value 'asd' of element 'empnumber' is not valid., Line : -1, Column : -1
]. Exchange[ID-GBSMIXDEV01-uk-oup-com-46713-1511957485149-83-2][Message: <empRecord>
<employee>
<empnumber>asd</empnumber>
<surname>PM</surname>
<firstname>Abhinay</firstname>
</employee>
</empRecord>]
I am getting the correct exception but I dont want the below part in the exception.
Is there any way I can remove these from the exception message?
Exchange[ID-GBSMIXDEV01-uk-oup-com-46713-1511957485149-83-2][Message: <empRecord>
<employee>
<empnumber>asd</empnumber>
<surname>PM</surname>
<firstname>Abhinay</firstname>
</employee>
</empRecord>]
That message is coming from the exception, so you can manipulate the string inside a Processor.
Write a simple class to do this:
private static final Logger logger_ = LoggerFactory
.getLogger(ExceptionMsgProcessor.class);
public void process(Exchange exchange) {
Exception e = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
String msg = e.getMessage();
// manipulate the string here
log.info("Exception message: {}", msg);
}
And then send the Exception to this bean
<doTry>
<to uri="validator:http://localhost/employee.xsd"/>
<doCatch>
<exception>java.lang.Exception</exception>
<to uri="bean:exceptionMsgProcessor" />
</doCatch>
</doTry>

Return original exception to the client instead of returning CamelExecutionException Apache Camel

I have a camel context defined inside an XML based spring context file.
There is one route, which is being invoked by the SOAP(XML) web service client bean.
While calling the route, it throws some exception and the client receives Camel Exception, instead of the original exception.
Camel response to the client on exception is shown below
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Exception occurred during execution on the exchange: Exchange[ID-CZC4101XJV-53724-1497351782614-9-2]</faultstring>
</soap:Fault>
Expected response should be the original exception message on
any exception
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>blah blah blah Number Not valid, I am the original exception</faultstring>
</soap:Fault>
Here is my camel route definition
<route>
<from uri="direct:remove" />
<to uri="bean:removeBean?method=validationNubmer" />
<to uri="bean:removeBean?method=checkBusiness" />
<to uri="bean:removeBean?method=triggerRemove" />
</route>
And below the SOAP bean operation which sends an XML request to the
route
public void removeMe(String id) {
RemoveRequest request = new RemoveRequest();
request.setId(id);
producer.requestBody("direct:remove", request);
}
I read the exception clause topic from the Apache Camel documentation, but I couldn't find any information on how to return the original exception to the client, sad!! any help?
I tried using camel onException but no luck :(
<onException>
<exception>java.lang.Exception</exception>
<redeliveryPolicy maximumRedeliveries="0" />
<handled>
<constant>true</constant>
</handled>
<transform>
<simple>Error Occurred: ${exception.message}</simple>
</transform>
</onException>
You can turn on handleFault to let exceptions automatic be transformed to SOAP faults. See Camel in Action 2 book, chapter 11, which has a little example: https://github.com/camelinaction/camelinaction2/tree/master/chapter11/errorhandler#1136---handling-faults
Or you need to use <setFaultBody> instead of <transform> to tell Camel you are setting the message body as a SOAP fault.

No message body reader has been found [duplicate]

I have a jaxrs client configured like this:
<jaxrs:client id="opaRestProxy" name="opaRestProxy"
address="${endpoint}" serviceClass="com.test.RestProxy"
inheritHeaders="true" threadSafe="true">
<jaxrs:headers>
<entry key="Accept" value="application/json" />
<entry key="Content-Type" value="application/json" />
</jaxrs:headers>
</jaxrs:client>
But when I send a request I get the following exception:
Caused by: org.apache.cxf.interceptor.Fault: .No message body writer has been found for class : class com.test.RequestObject, ContentType : application/json.
at org.apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter.handleMessage(ClientProxyImpl.java:646)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.jaxrs.client.ClientProxyImpl.doChainedInvocation(ClientProxyImpl.java:527)
... 47 more
My RestProxy class looks like this:
#Component
public interface RestProxy {
#POST
#Path("/getSomething")
String getSomething(RequestObject RequestObject);
}
If you are using Jackson JSON library you need to add these xml tags to your application context.
<jaxrs:providers>
<bean id="jacksonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
</jaxrs:providers>
If you are using any other library add that bean to the providers tag. Hope that helps!
If you are consuming using javax.ws.rs.client.Client, please register the provider using client.register(new JacksonJsonProvider());
This answers point me in the right direction, yet i had to add on two parts to make it work on web.xml
<init-param>
<param-name>jaxrs.providers</param-name>
<param-value>
org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider
(writeXsiType=false)
</param-value>
</init-param>
And on the client call:
List<Object> providers = new ArrayList<>();
// add custom providers if any
providers.add(new JacksonJaxbJsonProvider());
WebClient client = WebClient.create(ENDPOINT_ADDRESS,providers);

Binary File To SQL Database Apache Camel

I need some guidance around which approach to use to load binary files from a folder into a MySQL Database using Camel. Basically I want to store voice logs from our PBX system into a database. The directory with the voice logs will be a remote directory
I have designed a prototype but I am not sure if this is really efficient, it works but I am not happy with the design. Let me explain what I am doing. Camel route as follows:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>com.hia.camelone</package>
<route>
<from uri="file://c:/CTest/Inbox?noop=true&recursive=true&delay=3000"/>
<to uri="bean://fileToSQL"/>
<to uri="jdbc://timlogdb"/>
</route>
</camelContext>
<bean id="timlogdb" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value=" com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/TimLog" />
<property name="username" value="root" />
<property name="password" value="blahblah" />
</bean>
<bean id="fileToSQL" class="com.hia.camelone.fileToSQL"/>
And the code to fileToSQL bean is:
public class fileToSQL {
public String toString(#Headers Map<String,Object> header, #Body Object body){
StringBuilder sb = new StringBuilder();
String filename =(String)header.get("CamelFileNameOnly");
String escapedFileName = StringEscapeUtils.escapeJava(filename).replace("\'", "");
String filePath = StringEscapeUtils.escapeJava((String)header.get("CamelFilePath"));
sb.append("insert into FileLog ");
sb.append("(FileName,FileData) values (");
sb.append("'").append(escapedFileName).append("',").append("LOAD_FILE(\"").append(filePath).append("\")");
sb.append(")");
System.out.println(sb.toString());
System.out.println(body);
System.out.println(header.toString());
return sb.toString();
}
}
Ok short explanation I get the file component to consume the files then I build a SQL string using the MySQL LOAD_FILE() function to load the file.
My thoughts around this:
The LOAD_FILE function only works on the local machine and thus this route will only with the files being on the local machine. I could use a file producer to copy the files from some remote directory to a local directory and then use the route. My route would be something like this then:
<route>
<from uri="file://c:/CTest/Inbox?noop=true&recursive=true&delay=3000"/>
<to uri="file://c:/outbox"/>
<to uri="bean://fileToSQL"/>
<to uri="jdbc://timlogdb"/>
</route>
However since I have access to the files content in the message from the files consumer I should be able to theoretically be able to access the body/content of the string and build a SQL command that does NOT use the LOAD_FILE() function.
The only way I know how to build such a string is by using the prepared statement of JDBC. This would be first prize if I could somehow build a insert statement with the content from the file consumer.
Can I create a prepared statement in my fileToSQL bean and pass it to my jdbc component?
Or how do I build a INSERT statement without the LOAD_FILE() function?
Since I have to use the LOAD_FILE() function I would now have to cater for both unix and windows filepaths. While this should not be difficult I just dont like the idea of putting OS specific code into my applications(feels like a work around).
Anybody here ever uploaded binary files to a MySQL database using Camel who can give me some guidance on the points above. While I could work around the problems I just want to make sure I dont miss a obvious way of doing things.
I had a look around here and only found people working with mostly text files. Guys please don't even go down the route of me storing the file on the files system and linking it to the database. We have some very specific disaster recovery requirements and legal requirements that enforce the need for me to store it in a database.
Right so I managed to find a way and it was not that difficult. What I essentially did was get rid of the JDBC Camel Component in the route. I then injected the data source bean into my fileToSQL bean. I then used a simple prepared statement to insert the file and its name into MySQL.
As always code is much more explicit than my english.
<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>com.hia.camelone</package>
<route>
<from uri="file://c:/CTest/Inbox?noop=true&recursive=true&delay=3000"/>
<to uri="bean://fileToSQL"/>
<!--<to uri="jdbc://timlogdb"/>-->
</route>
</camelContext>
<bean id="timlogdb" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value=" com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/TimLog" />
<property name="username" value="root" />
<property name="password" value="lalala" />
</bean>
<bean id="fileToSQL" class="com.hia.camelone.fileToSQL">
<property name="dataSource" ref="timlogdb"/>
</bean>
As you can see I inject my timlogdb bean into my fileToSQL bean. Spring ROCKS!
So here is my fileToSQL bean.
public class fileToSQL {
private DriverManagerDataSource dataSource;
private static final String SQL_INSERT="insert into FileLog(FileName,FileData)values(?,?)";
#Handler
public void toString(#Headers Map<String,Object> header,Exchange exchange){
Connection conn = null;
PreparedStatement stmt=null;
String filename =StringEscapeUtils.escapeJava(((String)header.get("CamelFileNameOnly")).replace("\'", ""));
try {
conn= dataSource.getConnection();
stmt =conn.prepareStatement(SQL_INSERT);
stmt.setString(1, filename);
byte[] filedata = exchange.getIn().getBody(byte[].class);
stmt.setBytes(2,filedata );
int s = stmt.executeUpdate();
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
finally{
try
{
if (stmt!=null)
{
stmt.close();
}
if (conn!=null)
{
conn.close();
}
}
catch(SQLException e)
{
System.out.println(e.getMessage());
}
}
}
/**
* #param dataSource the dataSource to set
*/
public void setDataSource(DriverManagerDataSource dataSource) {
this.dataSource = dataSource;
}
}
The guys from Camel did a great job. Camel is truly flexible especially when you combine it with Spring.
What a ride!

How to log exceptions with network targets in NLog

I am using the NLog logging framework and am trying to get exception and stacktrace information showing up in any UDP logger appliaction, such as Sentinel and Log2Console, but can only get the log message part displayed. Outputting to a file works well as most examples do just that, so the problem revolves around using network targets with NLog.
Bonus if a custom format can be applied on inner exceptions and stacktrace, but this is not required. Exception.ToString() would go a long way.
Note on the example code: With Log2Console I found an article on how to send exception as a separate log entry. Although this worked, I was not happy with the solution.
Example exception logging code:
Logger Log = LogManager.GetCurrentClassLogger();
try
{
throw new InvalidOperationException("My ex", new FileNotFoundException("My inner ex1", new AccessViolationException("Innermost ex")));
}
catch (Exception e)
{
Log.ErrorException("TEST", e);
}
Example NLog.config:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets async="true">
<!-- Send by UDP to Sentinel with NLogViewer protocol -->
<target name="network" xsi:type="NLogViewer" address="udp://192.168.1.3:9999" layout="${message}${onexception:inner=${newline}${exception:format=tostring}}" />
<!-- Send message by UDP to Log2Console with Chainsaw protocol -->
<target name="network2" xsi:type="Chainsaw" address="udp://192.168.1.3:9998" appinfo="Grocelist"/>
<!-- Send exception/stacktrace by UDP to Log2Console with generic network protocol -->
<target name="network2ex" xsi:type="Network" address="udp4://192.168.1.3:9998" layout="${exception:format=ToString}" />
<target name="logfile" xsi:type="File" layout="${longdate}|${level:uppercase=true}|${logger}|${message}|${exception:format=tostring}"
createDirs="true"
fileName="${basedir}/logs/${shortdate}.log"
/>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
<logger name="*" minlevel="Debug" writeTo="network" />
<logger name="*" minlevel="Debug" writeTo="network2" />
<logger name="*" minlevel="Warn" writeTo="network2ex" />
</rules>
</nlog>
Some links:
http://nlog-project.org
http://nlog-project.org/wiki/Targets
http://nlog-project.org/wiki/Exception_layout_renderer
http://nlog-project.org/2011/04/20/exception-logging-enhancements.html
http://nlog-project.org/wiki/How_to_properly_log_exceptions%3F
How to tell NLog to log exceptions?
https://stackoverflow.com/a/9684111/134761
http://nlog-forum.1685105.n2.nabble.com/How-to-send-stacktrace-of-exceptions-to-Chainsaw-or-Log2Console-td5465045.html
Edit:
After searching some more this seems to be a limitation on NLog's end. A recent patch is apparently out there: log4jxmlevent does not render Exception
Edit2:
I rebuilt NLog with patch, but it did not seem to help in Sentinel or Log2Console apps. I might have to try log4net to make sure those apps really do support what I am trying to achieve.
Edit3:
I currently use string.Format() to join and format message and exception text myself. This works well, but is not what I'm looking for here.
You can also extend NLog to include exceptions for network logging.
Create an extended layout:
[Layout("Log4JXmlEventLayoutEx")]
public class Log4JXmlEventLayoutEx : Log4JXmlEventLayout
{
protected override string GetFormattedMessage(LogEventInfo logEvent)
{
string msg = logEvent.Message + " ${exception:format=Message,Type,ToString,StackTrace}";
msg = SimpleLayout.Evaluate(msg, logEvent);
LogEventInfo updatedInfo;
if (msg == logEvent.Message) {
updatedInfo = logEvent;
} else {
updatedInfo = new LogEventInfo(
logEvent.Level, logEvent.LoggerName,
logEvent.FormatProvider, msg,
logEvent.Parameters, logEvent.Exception);
}
return base.GetFormattedMessage(updatedInfo);
}
}
Create a target that uses that layout
[Target("NLogViewerEx")]
public class NLogViewerTargetEx : NLogViewerTarget
{
private readonly Log4JXmlEventLayoutEx layout = new Log4JXmlEventLayoutEx();
public override Layout Layout { get { return layout; } set {} }
}
Update NLog.config:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="Assembly.Name.That.Contains.Extended.Target"/>
</extensions>
<targets>
<target name="logViewer"
xsi:type="NLogViewerEx"
address="udp://localhost:7071">
</targets>
...
</nlog>
A few years later and this is pretty trivial, try adding
includeSourceInfo="true"
to your target file, so it looks like;
<target name="viewer"
xsi:type="NLogViewer"
includeSourceInfo="true"
address="udp://127.0.0.1:9999" />
Gives you Source File, Line, Class and Method info.
I had this problem, and just updated my NLog nuget package to 2.0.1.2
Now I have exceptions coming through to Log2Console just fine.
Have you tried the latest developer snapshot of Chainsaw? It will display stack traces and supports log4net/UDP appenders, and according to NLog you can use it as well:
http://nlog-project.org/wiki/Chainsaw_target
Try the latest developer snapshot, has a ton of features: http://people.apache.org/~sdeboy
Just download and build the latest (NLog-Build-2.0.0.2007-0-g72f6495) sources from GitHub: https://github.com/jkowalski/NLog/tree/
This issue is fixed there by NLog developer.
In your NLog.config modify the target like the following.
<target name="file" xsi:type="File" fileName="log.txt" layout="${longdate}:${message} ${exception:format=message,stacktrace:separator=*}" />
The part that you are looking for is
${exception:format=message,stacktrace:separator=*}
For more information on this look here.