jaxb - how to know when eventhandler is called (during validation) - jaxb

I am parsing XML with JAXB and have created an event handler that will display errors if there were issues on validation.
The event handler is called and prints out errors; How do I throw an exception if the event handler is called (after printout?)
In the code, I have no idea when the event handler is called, it is simply called on a validation error; I need the ability to move a file to a /dir/ after the event handler returns.
My event handler looks like this:
import base.helper.HelperBase;
import org.springframework.stereotype.Component;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import java.util.logging.Level;
/**
*
*/
#Component
public class MyValidationEventHandler extends HelperBase implements ValidationEventHandler {
public boolean handleEvent(ValidationEvent event) {
logger.log(Level.INFO, "\n---");
System.out.println("EVENT");
System.out.println("\nEVENT");
System.out.println("SEVERITY: " + event.getSeverity());
System.out.println("MESSAGE: " + event.getMessage());
System.out.println("LINKED EXCEPTION: " + event.getLinkedException());
System.out.println("LOCATOR");
System.out.println(" LINE NUMBER: " + event.getLocator().getLineNumber());
System.out.println(" COLUMN NUMBER: " + event.getLocator().getColumnNumber());
System.out.println(" OFFSET: " + event.getLocator().getOffset());
System.out.println(" OBJECT: " + event.getLocator().getObject());
System.out.println(" NODE: " + event.getLocator().getNode());
System.out.println(" URL: " + event.getLocator().getURL());
new Exception("fail");
return true;
}
}
When processing, my code looks like this:
private void processXmlFile(String file) throws Exception {
// todo: test for file existence, get size, print stats
try {
logger.log(Level.INFO, "Processing: " + file);
SchemaFactory sf = null;
Schema schema = null;
JAXBContext jctx = JAXBContext.newInstance("mypackage.jaxb");
Unmarshaller unmarshaller = jctx.createUnmarshaller();
if (validate) {
sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schema = sf.newSchema(new File(xsd));
unmarshaller.setSchema(schema);
eventHandler.setLogger(logger);
unmarshaller.setEventHandler(eventHandler);
}
JAXBElement<MyType> mytype = unmarshaller.unmarshal(new StreamSource(new File(file)), MyType.class);
MyType ct = mytype.getValue();
} catch (Exception e) { // if find a problem file, just move it out of the way and keep processing
// if the event handler is called, I want to throw an exception and do something here.
// move file to failed
fileUtils.moveFile(config.getErrorDir(), file);
// on an unmarshall failure, this exception is not thrown/caught because the event handler handles things and returns true
}
}

Please read How to Throw Exceptions.
In your event handler you need to throw() an Exception perhaps with something like:
throw new ValidationException(); // throw exeption
Instead of:
new Exception("fail"); // create exception but do nothing with it?
And you would define your ValidationException as:
public class ValidationException extends RuntimeException {
public ValidationException(final String s) {
super(s);
}
Change:
public boolean handleEvent(ValidationEvent event) {
To:
public boolean handleEvent(ValidationEvent event) throws ValidationException {
In processXmlFile() we now need something like:
catch (ValidationException e) {
// catch more specific exception first
fileUtils.moveFile(config.getErrorDir(), file);
catch (Exception e) {
// deal with any other exceptions ...
}

Related

Spring Integration Default Response for Jms inboundGateway

Seeing the below exception when trying to send a default constructed response for Jms inboundGateway exception from the downstream call. We are extracting the failedMessage headers from the ErrorMessage and then setting the constructed response as payload. The replyChannel headers is matching with the initially logged message header
2023-01-26 20:34:32,623 [mqGatewayListenerContainer-1] WARN o.s.m.c.GenericMessagingTemplate$TemporaryReplyChannel - be776858594e7c79 Reply message received but the receiving thread has exited due to an exception while sending the request message:
ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: Failed to send or receive; nested exception is java.io.UncheckedIOException: java.net.SocketTimeoutException: Connect timed out, failedMessage=GenericMessage [payload=NOT_PRINTED, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#2454562d, b3=xxxxxxxxxxxx, nativeHeaders={}, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#2454562d, sourceTransacted=false, jms_correlationId=ID:xxxxxxxxxx, id=xxxxxxxxxx, jms_expiration=36000, timestamp=1674750867614}]
Code:
return IntegrationFlows.from(Jms.inboundGateway(mqGatewayListenerContainer)
.defaultReplyQueueName(replyQueue)
.replyChannel(mqReplyChannel)
.errorChannel(appErrorChannel)
.replyTimeout(mqReplyTimeoutSeconds * 1000L))
// log
.log(DEBUG, m -> "Request Headers: " + m.getHeaders() + ", Message: " + m.getPayload())
// transform with required response headers
.transform(Message.class, m -> MessageBuilder.withPayload(m.getPayload())
.setHeader(ERROR_CHANNEL, m.getHeaders().get(ERROR_CHANNEL))
.setHeader(REPLY_CHANNEL, m.getHeaders().get(REPLY_CHANNEL))
.setHeader(CORRELATION_ID, m.getHeaders().get(MESSAGE_ID))
.setHeader(EXPIRATION, mqReplyTimeoutSeconds * 1000L)
.setHeader(MSG_HDR_SOURCE_TRANSACTED, transacted)
.build())
return IntegrationFlows.from(appErrorChannel())
.publishSubscribeChannel(
pubSubSpec -> pubSubSpec.subscribe(sf -> sf.channel(globalErrorChannel))
.<MessagingException, Message<MessagingException>>
transform(AppMessageUtil::getFailedMessageWithoutHeadersAsPayload)
.transform(p -> "Failure")
.get();
public static Message<MessagingException> getFailedMessageAsPayload(final MessagingException messagingException) {
var failedMessage = messagingException.getFailedMessage();
var failedMessageHeaders = Objects.isNull(failedMessage) ? null : failedMessage.getHeaders();
return MessageBuilder.withPayload(messagingException)
.copyHeaders(failedMessageHeaders)
.build();
}
Since you perform the processing of the request message on the same thread, it is blocked on a send and therefore we just re-throw an exception as is:
try {
doSend(channel, requestMessage, sendTimeout);
}
catch (RuntimeException ex) {
tempReplyChannel.setSendFailed(true);
throw ex;
}
And as you see we mark that tempReplyChannel as failed on a send operation.
So, the replyChannel header correlated with that mqReplyChannel is out of use. If you get rid of it at all, then everything is OK. But you also cannot reply back an Exception since the framework treats it as an error to re-throw back to the listener container:
if (errorFlowReply != null && errorFlowReply.getPayload() instanceof Throwable) {
rethrow((Throwable) errorFlowReply.getPayload(), "error flow returned an Error Message");
}
So, here is a solution:
#SpringBootApplication
public class So75249125Application {
public static void main(String[] args) {
SpringApplication.run(So75249125Application.class, args);
}
#Bean
IntegrationFlow jmsFlow(ConnectionFactory connectionFactory) {
return IntegrationFlow.from(Jms.inboundGateway(connectionFactory)
.requestDestination("testDestination")
.errorChannel("appErrorChannel"))
.transform(payload -> {
throw new RuntimeException("intentional");
})
.get();
}
#Bean
IntegrationFlow errorFlow() {
return IntegrationFlow.from("appErrorChannel")
.transform(So75249125Application::getFailedMessageAsPayload)
.get();
}
public static Message<String> getFailedMessageAsPayload(MessagingException messagingException) {
var failedMessage = messagingException.getFailedMessage();
var failedMessageHeaders = failedMessage.getHeaders();
return MessageBuilder.withPayload("failed")
.copyHeaders(failedMessageHeaders)
.build();
}
}
and unit test:
#SpringBootTest
class So75249125ApplicationTests {
#Autowired
JmsTemplate jmsTemplate;
#Test
void errorFlowRepliesCorrectly() throws JMSException {
Message reply = this.jmsTemplate.sendAndReceive("testDestination", session -> session.createTextMessage("test"));
assertThat(reply.getBody(String.class)).isEqualTo("failed");
}
}
Or even better like this:
public static String getFailedMessageAsPayload(MessagingException messagingException) {
var failedMessage = messagingException.getFailedMessage();
return "Request for '" + failedMessage.getPayload() + "' has failed";
}
and this test:
#Test
void errorFlowRepliesCorrectly() throws JMSException {
String testData = "test";
Message reply = this.jmsTemplate.sendAndReceive("testDestination", session -> session.createTextMessage(testData));
assertThat(reply.getBody(String.class)).isEqualTo("Request for '" + testData + "' has failed");
}

Exception thrown from ExpressionEvaluatingRequestHandlerAdvice triggers error handler handler on Adapter

I set an advice on my `MessageHandler'
#ServiceActivator(inputChannel = "outbound",adviceChain = "expressionAdvice")
public MessageHandler...
and configured it as:
#Bean
public ExpressionEvaluatingRequestHandlerAdvicer expressionAdvice() {
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
advice.setFailureChannelName("failure");
return advice;
}
in failure handler I parse and detect the errors
#ServiceActivator(inputChannel = "failure")
public void handleFailures(Message<?> message) {
ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException adviceException = (ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException) message.getPayload();
Throwable cause = adviceException.getCause().getCause().getCause();
for specific errors I am doing some operations and flow is resumed.
But for specific error type I just log the error and continue, for other types I am rethrowing exception to get a retry.
This works, but there is a side affect, this throw Exception triggers ServiceActivator that was set on MessageProducerSupport.setErrorChannelName on the adapter.
#ServiceActivator(inputChannel = "onerror")
It does the job but I would like to avoid calling it, just to do the retries without going to this handler.
I do need this handler to catch other types of errors coming from source-channel.
See this option on that ExpressionEvaluatingRequestHandlerAdvice:
/**
* If true, any exception will be caught and null returned.
* Default false.
* #param trapException true to trap Exceptions.
*/
public void setTrapException(boolean trapException) {
More info in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#message-handler-advice-chain
UPDATE
For conditional exceptions "trapping", you need consider to implement a logic in your failure channel sub-flow. However trapException is still must be true.
This is the logic we have so far in the ExpressionEvaluatingRequestHandlerAdvice:
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
try {
Object result = callback.execute();
if (this.onSuccessExpression != null) {
evaluateSuccessExpression(message);
}
return result;
}
catch (RuntimeException e) {
Exception actualException = unwrapExceptionIfNecessary(e);
if (this.onFailureExpression != null) {
Object evalResult = evaluateFailureExpression(message, actualException);
if (this.returnFailureExpressionResult) {
return evalResult;
}
}
if (!this.trapException) {
if (e instanceof ThrowableHolderException) { // NOSONAR
throw (ThrowableHolderException) e;
}
else {
throw new ThrowableHolderException(actualException); // NOSONAR lost stack trace
}
}
return null;
}
}
So, we catch an exception for a callback.execute() and process it in the evaluateFailureExpression() (which may just send an ErrorMessage to the mentioned failureChannel). Such a this.messagingTemplate.send(this.failureChannel, errorMessage); is not wrapped into a try..catch, so if you re-throw an exception from your error handling flow, it is going to be bubbled to the main flow.

JSF Custom EL function - encapsulating an Exception

Following the example (based on a similar question):
/**
*
*/
package za.co.sindi.jsf.functions;
import java.io.IOException;
import org.markdown4j.Markdown4jProcessor;
/**
*
* #author Buhake Sindi
* #since 22 January 2013
*
*/
public final class SomeFunctions {
/**
* Private constructor
*/
private SomeFunctions() {
//TODO: Nothing...
}
public static String process(String input) {
SomeProcessor processor = new SomeProcessor();
try {
return processor.process(input);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace(); //I don't believe this is correct.
}
}
}
What do I do inside the catch block? Do I just log it on a java Logger or is there a preferred JSF way on encapsulating an Exception?
Depends on the concrete functional requirements.
If an empty output is acceptable, log it and return null.
public static String process(String input) {
SomeProcessor processor = new SomeProcessor();
try {
return processor.process(input);
} catch (IOException e) {
someLogger.warn("Processing markdown failed", e);
return null;
}
}
If it's not acceptable, throw it.
public static String process(String input) throws IOException {
SomeProcessor processor = new SomeProcessor();
return processor.process(input);
}
Unrelated to the concrete problem, you'd better process markdown and store as property (and ultimately also in DB) only once during create/update instead of processing the very same output again and again. So, effectively you end up with 2 properties / DB columns, one with raw markdown and another with parsed/processed markdown.

javafx-2 applet doesn't update gui when called from javascript

I'm trying to invoke a method from javascript object which in turn calls a the following java method :
public void loadPicture(final String absolutePath) {
System.out.println("loadPicture " + absolutePath);
Image dbimage;
dbimage = new Image(absolutePath, 100.0d, 100.0d, false, false);
final ImageView dbImageView = new ImageView();
dbImageView.setImage(dbimage);
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
System.out.println("hbox children : "+hbox.getChildren().size());
hbox.getChildren().add(dbImageView);
System.out.println("hbox children : "+hbox.getChildren().size());
//test
//logger.debug(" aggiunto "+absolutePath);
DropPictures.getPicturesNames().add(absolutePath);
} catch (Exception e) {
System.out.println("eccezione :" + e.getLocalizedMessage());
}
}
});
}
In javascript the method invocation is :
var a = document.getElementById(myDivId);
a.loadPicture();
I've traced the execution and the above method doesn't throw any exception,but it is run cause i see the output in java console, but the applet doesn't show the picture.
I've used Platform.runLater to update the GUI in the javafx thread, still no update is performed.

Getting null pointer exception while loading schema

public class TestValidatorSample {
public static void main(String aa[]) throws SAXException, IOException, ParserConfigurationException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder parser = dbf.newDocumentBuilder();
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// /test_elements.xsd
Schema schema = factory.newSchema(new StreamSource(TestValidatorSample.class.getResource(
"/xsds/pakagename/test_elements.xsd").toString()));
Validator validator = schema.newValidator();
DOMSource domsrc = new DOMSource(parser.parse(new InputSource("test-example.xml")));
try {
validator.validate(domsrc);
System.out.println("Validation successfull!!!");
// System.out.println(xmlFile.getSystemId() + " is valid");
} catch (SAXException e) {
System.out.println("Validation not successfull!!!");
// System.out.println(xmlFile.getSystemId() + " is NOT valid");
System.out.println("Reason: " + e.getLocalizedMessage());
}
}
}
Exception in thread "main" java.lang.NullPointerException at line 47.
I am getting a nullpointer(as mentioned above) exception while loading a schema.
In the above code snippet I am trying to load a schema which will be used to validate xmls generated in my application .
Can anybody please help me to find out why I am getting null pointer exception ?

Resources