Passing a Class for object serialization to xml - java

After reading both
How can I pass a Class as parameter and return a generic collection in Java?
and
How do I pass a class as a parameter in Java?
I am unsure how I would pass a JibX generated class as a parameter to a method that serializes the object.
I would like to accomplish something like the following.
protected static String SerializeObject( Class clazz , Object request)
{
String message = null;
try
{
IBindingFactory lBindingFactory = BindingDirectory.getFactory(
clazz.class);
IMarshallingContext lContext = lBindingFactory.
createMarshallingContext();
ByteArrayOutputStream lOutputStream = new ByteArrayOutputStream();
lContext.marshalDocument(request, "UTF-8", null,
lOutputStream);
message = new String(lOutputStream.toByteArray(), "UTF-8");
}
catch (JiBXException lEx)
{
throw new RuntimeException("Problems generating XML, " +
"underlying problem is " + lEx.getMessage(), lEx);
}
catch (UnsupportedEncodingException lEx)
{
throw new RuntimeException("Problems generating XML in specified" +
"encoding, underlying problem is " + lEx.getMessage(), lEx);
}
return message;
}
Where the class is only used to define what the BindingDirectory is using to serialize the object into.

I might be better to avoid attempting to pass the class at all. I can achieve the same affect by passing the IBindingFactory and adding a bit of code to the caller.
The result would end up like this.
protected static String SerializeObject( IBindingFactory lBindingFactory,
Object request)
{
String message = null;
try
{
IMarshallingContext lContext = lBindingFactory.
createMarshallingContext();
ByteArrayOutputStream lOutputStream = new ByteArrayOutputStream();
lContext.marshalDocument(request, "UTF-8", null,
lOutputStream);
message = new String(lOutputStream.toByteArray(), "UTF-8");
}
catch (JiBXException lEx)
{
throw new RuntimeException("Problems generating XML, " +
"underlying problem is " + lEx.getMessage(), lEx);
}
catch (UnsupportedEncodingException lEx)
{
throw new RuntimeException("Problems generating XML in specified" +
"encoding, underlying problem is " + lEx.getMessage(), lEx);
}
return message;
}

Related

HAPI HL7 Validation throws Exceptions

I am working on a Java endpoint that I intend to use for HL7 message validation. I have a basic app running that uses a variation of the standard HAPI HL7 validation example. If I pass in a valid message I get the "Success" response. If I pass in a invalid message I still get a "Success" response.
The only way I get an error response is if the HL7 is badly formatted and the PipeParser throws an exception. In that case it gets caught in the catch block.
What I want to see is if I pass in an invalid message that it actually gets validated and returns all the validation errors. But I don't ever actually see any validation. It either parses or crashes trying to parse.
What am I missing here?
HapiContext context = new DefaultHapiContext();
ValidationContext validationContext = ValidationContextFactory.defaultValidation();
context.setValidationContext(validationContext);
try
{
context.getParserConfiguration().setUnexpectedSegmentBehaviour(UnexpectedSegmentBehaviourEnum.THROW_HL7_EXCEPTION);
Message messageValidationResults = context.getPipeParser().parse(hl7Message);
SimpleValidationExceptionHandler handler = new SimpleValidationExceptionHandler(context);
handler.setMinimumSeverityToCollect(Severity.INFO);
Validator<Boolean> validator = context.getMessageValidator();
if (!validator.validate(messageValidationResults, handler))
{
if (handler.getExceptions().size() == 0)
{
hl7ValidationResult = "SUCCESS - Message Validated Successfully";
}
else
{
hl7ValidationResult = "ERROR - Found " + handler.getExceptions().size() + " problems\n\n";
for (Exception e : handler.getExceptions())
{
hl7ValidationResult += (e.getClass().getSimpleName() + " - " + e.getMessage()) + "\n";
}
}
}
}
catch (Exception e)
{
hl7ValidationResult = "ERROR - " + e.getMessage();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String sStackTrace = sw.toString();
hl7ValidationResult += "\n\n" + sStackTrace;
}
Please ignore the answer if do you think is not correct, I stopped to work with HL7 but, looking at my old project I have found this and maybe it can help you to find the solution of your problem:
{
DefaultValidationBuilder builder = new DefaultValidationBuilder() {
private static final long serialVersionUID = 1L;
#Override
protected void configure() {
super.configure();
forVersion(Version.V26);
}
};
HapiContext context = new DefaultHapiContext();
context.setValidationRuleBuilder(builder);
PipeParser hapiParser = context.getPipeParser();
try {
hapiParser.parse(hl7Message);
} catch (ca.uhn.hl7v2.HL7Exception e) {
// String error, String language, String requisitionNumber, String controlId, String processinId, String senderApplication, String senderFacility
errors.add(new HL7ValidationError(
"HAPI Validator error found: " + e.getMessage(),
extractor.accessPatientDirectly().getLanguage(),
extractor.accessPatientDirectly().getRequisitionNumber(),
extractor.accessPatientDirectly().getControlID(),
"",
extractor.accessPatientDirectly().getSenderApplication(),
extractor.accessPatientDirectly().getSenderFacility())
);
log.debug("HAPI Validator error found: " + e.getMessage());
}
try {
context.close();
}
catch (Exception ex) {
log.debug("Unable to close HapiContext(): " + ex.getMessage());
}
}
Basically I used hapiParser.parse(hl7Message); and catch the HL7Exception

Spring Jaxb2: How to append batch data to XML file with no reading it to memory?

I need to write data to xml in batches.
There are following domain objects:
#XmlRootElement(name = "country")
public class Country {
#XmlElements({#XmlElement(name = "town", type = Town.class)})
private Collection<Town> towns = new ArrayList<>();
....
}
And:
#XmlRootElement(name = "town")
public class Town {
#XmlElement
private String townName;
// etc
}
I'm marhalling objects with Jaxb2. Configuration as follows:
marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Country.class, Town.class);
Because simple marshalling doesn't work here as marhaller.marshall(fileName, country) - it malformes xml.
Is there a way to tweek marhaller so that it would create file if it's not exists with all marhalled data or if exists just append it at the end of xml file ?
Also as this files are potentially large I don't want to read whole file in memory, append data and then write to disk.
I've used StAX for xml processing as it stream based, consumes less memory then DOM and has ability to read and write comparing to SAX which can only parse xml data, but can't write it.
The is the approach I came up with:
public enum StAXBatchWriter {
INSTANCE;
private static final Logger LOGGER = LoggerFactory.getLogger(StAXBatchWriter.class);
public void writeUrls(File original, Collection<Town> towns) {
XMLEventReader eventReader = null;
XMLEventWriter eventWriter = null;
try {
String originalPath = original.getPath();
File from = new File(original.getParent() + "/old-" + original.getName());
boolean isRenamed = original.renameTo(from);
if (!isRenamed)
throw new IllegalStateException("Failed to rename file: " + original.getPath() + " to " + from.getPath());
File to = new File(originalPath);
XMLInputFactory inFactory = XMLInputFactory.newInstance();
eventReader = inFactory.createXMLEventReader(new FileInputStream(from));
XMLOutputFactory outFactory = XMLOutputFactory.newInstance();
eventWriter = outFactory.createXMLEventWriter(new FileWriter(to));
XMLEventFactory eventFactory = XMLEventFactory.newInstance();
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
eventWriter.add(event);
if (event.getEventType() == XMLEvent.START_ELEMENT && event.asStartElement().getName().toString().contains("country")) {
for (Town town : towns) {
writeTown(eventWriter, eventFactory, town);
}
}
}
boolean isDeleted = from.delete();
if (!isDeleted)
throw new IllegalStateException("Failed to delete old file: " + from.getPath());
} catch (IOException | XMLStreamException e) {
LOGGER.error(e.getMessage(), e);
throw new RuntimeException(e);
} finally {
try {
if (eventReader != null)
eventReader.close();
} catch (XMLStreamException e) {
LOGGER.error(e.getMessage(), e);
}
try {
if (eventWriter != null)
eventWriter.close();
} catch (XMLStreamException e) {
LOGGER.error(e.getMessage(), e);
}
}
}
private void writeTown(XMLEventWriter eventWriter, XMLEventFactory eventFactory, Town town) throws XMLStreamException {
eventWriter.add(eventFactory.createStartElement("", null, "town"));
// write town id
eventWriter.add(eventFactory.createStartElement("", null, "id"));
eventWriter.add(eventFactory.createCharacters(town.getId()));
eventWriter.add(eventFactory.createEndElement("", null, "id"));
//write town name
if (StringUtils.isNotEmpty(town.getName())) {
eventWriter.add(eventFactory.createStartElement("", null, "name"));
eventWriter.add(eventFactory.createCharacters(town.getName()));
eventWriter.add(eventFactory.createEndElement("", null, "name"));
}
// write other fields
eventWriter.add(eventFactory.createEndElement("", null, "town"));
}
}
It's not the best approach, dispite the fact that it's stream based and it's working, it has some overhead. When a batch will be added - the old file has to be re-read.
It will be nice to have an option to append the data at some point in file (like "append data to that file after 4 line"), but seems this can't be done.

Test using Mockito

Here is my code and I want to know what's the best way to test using Mockito as I am creating couple of objects using new keyword. can anyone guide me ?
public static PDDocument generatePDF(final String reportString, final String requestId) throws IOException {
final PDDocument document = new PDDocument();
final byte[] byteStr = reportString.getBytes(StandardCharsets.UTF_8);
final String str = new String(byteStr,
StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(new StringReader(str));
try {
// PDF box ceremony
final TextToPDF textToPdf = new TextToPDF();
textToPdf.setFont(PDType1Font.COURIER);
textToPdf.setFontSize(10);
textToPdf.createPDFFromText(document, reader);
reader.close();
} catch (final IOException ioException) {
LOGGER.error("IO Exception while generating PDF for request id " + requestId, ioException.getMessage());
throw ioException;
} catch (final Exception e) {
LOGGER.error("Exception while generating PDF for request id " + requestId, e.getMessage());
throw e;
} finally {
reader.close();
}
return document;
}
Mockito is intended to mock the collaborators of the class/method you want to test. Note that it should be used only to mock the types you own.
In this case, you don't actually need Mockito.
An example, similar to yours, in which you could use Mockito, is this:
class PDFGenerator {
private ITextToPdf textToPdf; // This is an hypotetical interface provided by you, for example used as a wrapper to easily change the underling framework
public void setTextToPdf(ITextToPdf textToPdf) {
this.textToPdf = textToPdf;
}
public static PDDocument generatePDF(final String reportString, final String requestId) throws IOException {
final byte[] byteStr = reportString.getBytes(StandardCharsets.UTF_8);
final String str = new String(byteStr,
StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(new StringReader(str));
try {
IDocument document = textToPdf.createPDFFromText(reader);
reader.close();
return document;
} catch (final IOException ioException) {
LOGGER.error("IO Exception while generating PDF for request id " + requestId, ioException.getMessage());
throw ioException;
} catch (final Exception e) {
LOGGER.error("Exception while generating PDF for request id " + requestId, e.getMessage());
throw e;
} finally {
reader.close();
}
}
}
In this case, the test would be:
#Test
public void testGeneratePdf() throws Exception {
ITextToPdf textToPdfMock Mockito.mock(ITextToPdf.class);
PDFGenerator pdfGenerator = new PDFGenerator();
pdfGenerator.setTextToPdf(textToPdfMock);
Mockito.when(textToPdfMock.createPDFFromText(Mockito.any())).thenReturn(something);
IDocument generatedDocument = pdfGenerator.generatePDF(createReportString(), "TestId");
Mockito.verify(textToPdfMock, Mockito.times(1)).createPDFFromText(Mockito.any());
Mockito.verifyNoMoreInteractions(textToPdfMock);
// Do also some standard junit asserts on the generatedDocument
}

Converting pojos to generic records in confluent.io to send through a KafkaProducer

I am completely new to Kafka and avro and trying to use the confluent package. We have existing POJOs we use for JPA and I'd like to be able to simply produce an instance of my POJOs without having to reflect each value into a generic record manually. I seem to be missing how this is done in the documentation.
The examples use a generic record and set each value one by one like so:
String key = "key1";
String userSchema = "{\"type\":\"record\"," +
"\"name\":\"myrecord\"," +
"\"fields\":[{\"name\":\"f1\",\"type\":\"string\"}]}";
Schema.Parser parser = new Schema.Parser();
Schema schema = parser.parse(userSchema);
GenericRecord avroRecord = new GenericData.Record(schema);
avroRecord.put("f1", "value1");
record = new ProducerRecord<Object, Object>("topic1", key, avroRecord);
try {
producer.send(record);
} catch(SerializationException e) {
// may need to do something with it
}
There are several examples for getting a schema from a class and I found the annotations to alter that schema as necessary. Now how do I take an instance of a POJO and just send it to the serializer as is and have the library do the work of matching up the schema from the class and then copying the values into a generic record? Am I going about this all wrong? What I want to end up doing is something like this:
String key = "key1";
Schema schema = ReflectData.get().getSchema(myObject.getClass());
GenericRecord avroRecord = ReflectData.get().getRecord(myObject, schema);
record = new ProducerRecord<Object, Object>("topic1", key, avroRecord);
try {
producer.send(record);
} catch(SerializationException e) {
// may need to do something with it
}
Thanks!
I wound up creating my own serializer in this instance:
public class KafkaAvroReflectionSerializer extends KafkaAvroSerializer {
private final EncoderFactory encoderFactory = EncoderFactory.get();
#Override
protected byte[] serializeImpl(String subject, Object object) throws SerializationException {
//TODO: consider caching schemas
Schema schema = null;
if(object == null) {
return null;
} else {
try {
schema = ReflectData.get().getSchema(object.getClass());
int e = this.schemaRegistry.register(subject, schema);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(0);
out.write(ByteBuffer.allocate(4).putInt(e).array());
BinaryEncoder encoder = encoderFactory.directBinaryEncoder(out, null);
DatumWriter<Object> writer = new ReflectDatumWriter<>(schema);
writer.write(object, encoder);
encoder.flush();
out.close();
byte[] bytes = out.toByteArray();
return bytes;
} catch (IOException ioe) {
throw new SerializationException("Error serializing Avro message", ioe);
} catch (RestClientException rce) {
throw new SerializationException("Error registering Avro schema: " + schema, rce);
} catch (RuntimeException re) {
throw new SerializationException("Error serializing Avro message", re);
}
}
}
}

Jackson JSON to JAVA conversion problems

So i am using Jackson to convert objects to JSON and then send them over a connection and convert them back to objects on the other side of the connection. I ran into a problem when reading the JSON and attempting to turn it into a JAVA object it successfully changes JSON into multiple objects. It is easier to see in an example:
ObjectMapper map = new ObjectMapper();
Boolean test1 = null;
String test2 = null;
Integer test3 = null;
Boolean obj = false;
byte[] bytes = null;
try {
bytes = map.writeValueAsBytes(obj);
} catch (Exception e) {
e.printStackTrace();
}
try {
test1 = map.readValue(bytes, Boolean.class);
test2 = map.readValue(bytes, String.class);
test3 = map.readValue(bytes, Integer.class);
} catch (Exception e)
{
System.out.println("FAILED");
}
System.out.println("test1: " + test1 + "\ntest2: " + test2 + "\ntest3: " + test3);
And the output:
FAILED
test1: false
test2: false
test3: null
When attempting to convert the JSON Boolean to a String it was successful which is problematic for me because my current method looks similar to what is below and when the object deserialize is of the wrong type it causes problems down the road.
public void JSONtoJAVA(Class<?> clazz)
{
for(Event event : Events)
{
try
{
Object deserialized = map.readValue(bytes, event.getC());
Event.getMethod().invoke(deserialized);
}
catch(Exception e)
{
//Failed ignore
}
}
}
Your help is appreciated!

Categories

Resources