how to add image in email velocity transformer templates from classpath - smtp

I am using Velocity Transformer email template with my Mule smtp. Is there any ways that I can add images in the email templates from my classpath ?
That is for example .. if I have an image say abc.png in my classpath, can I able to use it in my velocity email template like < image src= ......

You can add outbound attachments to the Mule Message, using classpath resources as their source. These Mule Message attachments will be turned into MIME parts by the SMTP outbound transformer.
From the discussion here Embedding images into html email with java mail it seems you need to declare the images like this:
<img src=\"cid:uniqueImageID\"/>
You have to use a unique ID after cid: that is consistent with the Content-ID part header. Mule allows you to specify custom part headers by adding an outbound message property java.util.Map named attachmentName+"Headers" (attachmentName is the name of the outbound attachment).
One potential difficulty is that the code in the ObjectToMimeMessage transformer that takes care of transforming a the javax.activation.DataHandler (coming from the Mule Message outbound attachment) in a javax.mail.BodyPart only calls setFileName but not setDisposition which I think is needed for the image to show properly. This said, I'm not an expert here, you probably know more about properly generating MIME emails with attached images.

1) Embed the image Base64 encoded in your HTML
e.g.
Use following site to convert image to base64:
http://www.dailycoding.com/Utils/Converter/ImageToBase64.aspx

I had followed your code to add image path in the velocity transformer in the following way, the String logo will get the value from spring beans
public final class MessageTransformer extends AbstractMessageTransformer
{
private VelocityEngine velocityEngine;
private String templateName;
private Template template;
//This part is for getting the value from property file by declaring setter and getter for fileName and subscriberName
private String logo;
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
//This part is for getting template for email from classpath configured in mule flow
public VelocityMessageTransformer()
{
registerSourceType(Object.class);
setReturnDataType(new SimpleDataType<String>(String.class));
}
public void setVelocityEngine(final VelocityEngine velocityEngine)
{
this.velocityEngine = velocityEngine;
}
public void setTemplateName(final String templateName)
{
this.templateName = templateName;
}
#Override
public void initialise() throws InitialisationException
{
try
{
template = velocityEngine.getTemplate(templateName);
}
catch (final Exception e)
{
throw new InitialisationException(e, this);
}
}
#Override
public Object transformMessage(final MuleMessage message, final String outputEncoding)throws TransformerException
{
try
{
final StringWriter result = new StringWriter();
FileDataSource myFile = new FileDataSource (new File (logo)); // It contains path of image file
message.setOutboundProperty("logo", myFile);
// -------------------------------------------------------
final Map<String, Object> context = new HashMap<String, Object>();
context.put("message", message);
context.put("payload", message.getPayload());
context.put("logo", message.getOutboundProperty("logo"));
template.merge(new VelocityContext(context), result); //Merging all the attributes
System.out.println("MAIL WITH TEMPLATE SEND SUCCESSFULLY !!!");
System.out.println( result.toString() );
return result.toString();
}
catch (final Exception e)
{
throw new TransformerException(
MessageFactory.createStaticMessage("Can not transform message with template: " + template)
, e);
}
}
}

Related

Apache Camel: Unit testing for file and http components

I am fairly new to Camel & just managed to implement a use case as below with 2 routes which is using file & http components. Looking for some leads on writing junits for the same. Have tried some sample test case below based on the inputs that i found on the net. Not sure if that suffices. Appreciate your help!
Implementation:
#Override
public void configure() throws Exception {
// Global Exception Handling block
onException(FileWatcherException.class).process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println("Exception handled");
}
}).to("file:C:/error?recursive=true").handled(true);
// Actively listen to the input folder for an incoming file
from("file:C:/input?noop=true&recursive=true&delete=true")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String fileName = exchange.getIn().getHeader("CamelFileName").toString();
exchange.getIn().setHeader("fileName", fileName);
}
})
// Call the Get endpoint with fileName as input parameter
.setHeader(Exchange.HTTP_METHOD, simple("GET"))
.toD("http://localhost:8090/fileWatcher?fileName=${header.fileName}")
.choice()
// if the API returns true, move the file to the outbox folder
.when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(constant(200)))
.to("file:C:/outbox?noop=true&recursive=true")
.endChoice()
// If the API's response code is other than 200, move the file to error folder
.otherwise()
.log("Moving the file to error folder")
.to("file:C:/error?recursive=true")
.end();
// Listen to the outbox folder for file arrival after it gets moved in the above step
from("file:C:/outbox?noop=true&recursive=true")
// Request Body for POST call is set in FileDetailsProcessor class
.process(new FileDetailsProcessor())
.marshal(jsonDataFormat)
.setHeader(Exchange.HTTP_METHOD, simple("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
// Call the Rest endpoint with fileName & filePath as RequestBody
.to("http://localhost:8090/fileWatcher")
.process(new MyProcessor())
.end();
}
Junit
#Test
public void checkFileWatcherFunctionality() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mocking all endpoints. **QUESTION** - Is this required?
mockEndpointsAndSkip("http://localhost:8090:fileWatcher?fileName=loan.csv");
mockEndpointsAndSkip("file:C:/processing");
mockEndpointsAndSkip("file:C:/error");
mockEndpointsAndSkip("http://localhost:8090:fileWatcher");
}
});
context.start();
// **QUESTION** - This is a GET call. Expecting only the HTTP status code from it. How to check that?
getMockEndpoint("mock:http://localhost:8090:fileWatcher?fileName=abc.txt").expectedBodyReceived();
// **QUESTION** - This is a POST call. How to send request body along? Expecting only the HTTP status code from it. How to check that?
getMockEndpoint("mock:http://localhost:8090:fileWatcher").expectedBodyReceived();
// **QUESTION** - Is this the right way to check?
getMockEndpoint("mock:file:C:/processing").expectedFileExists("loan.csv");;
template.sendBodyAndHeader("file:C:/inbound", "", Exchange.FILE_NAME, "loan.csv");
// QUESTION - What can be asserted now?
}
Also - How to write test cases for negative flow (exception scenario)? Looking for suggestions.
I have managed to draft the test case. Is this the right approach or can there be a better way?
This might be more of an integration test i suppose.
The issue i see now is that the test case doesn't report at the end (success or failure), instead it keeps waiting for file arrival in the input folder. What am i missing?
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class FileWatcherRouteBuilderTest extends CamelTestSupport {
#Autowired
private TestRestTemplate restTemplate;
#Override
public RoutesBuilder createRouteBuilder() throws Exception {
return new FileWatcherRouteBuilder();
}
#Test
public void testFileCopy() throws Exception {
template.sendBodyAndHeader("file:C:/inbound", "", Exchange.FILE_NAME, "abc.csv");
// Call the GET endpoint
ResponseEntity<String> getResponse = restTemplate.getForEntity("http:localhost:8090/fileWatcher?fileName=abc.csv",
String.class);
assertTrue("Get call is unsuccessful", getResponse.getStatusCode().is2xxSuccessful());
String response = getResponse.getBody();
assertTrue(!response.isEmpty());
// The file would have moved to output folder now.
File targetFile = new File("C:/processing");
assertTrue(targetFile.isDirectory());
assertEquals(1, targetFile.listFiles().length);
// Since we need to extract the file name, doing the below step
Exchange exchange = consumer.receive("file:C:/processing");
String fileName = exchange.getIn().getHeader("CamleFileName").toString();
// RequestBody needed for POST call
FileDetails fileDetails = new FileDetails(fileName, "C:/processing/"+fileName);
HttpHeaders headers = new HttpHeaders();
HttpEntity<FileDetails> request = new HttpEntity<FileDetails>(fileDetails, headers);
// Call the POST endpoint
ResponseEntity<String> postResponse = restTemplate.postForEntity("http://localhost:8090/fileWatcher", request, String.class);
assertTrue("Post call is unsuccessful", postResponse.getStatusCode().is2xxSuccessful());
// Asserting that after both the web service calls, the file is still available in the output folder
assertEquals(1, targetFile.listFiles().length);
}
}

Thymeleaf messages / translations in JSON / YAML files

I'm using Thymeleaf with Spring Boot 2.
Is it possible to provide message sources (translations) in YAML / JSON format instead of *.properties files ?
yes, you can do it by extending the AbstractMessageSource class. here a sample you can use as a starting point:
#Component("messageSource")
public class JsonMessageSource extends AbstractMessageSource {
private static final String DEFAULT_LOCALE_CODE = "en";
#Override
protected MessageFormat resolveCode(String key, Locale locale) {
String message = resolveUsingJsonOrYaml(key,locale); //you have to implement this this
if (message == null) {
message = resolveUsingJsonOrYaml(key,DEFAULT_LOCALE_CODE);
}
return new MessageFormat(message, locale);
}
}

Apache Camel CSV with Header

I have written a simple test app that reads records from a DB and puts the result in a csv file. So far it works fine but the column names i.e. headers are not put in the csv file. According to the doc it should be put there. I have also tried it without/with streaming and split but the situation is the same.
In the camel unit-tests in line 182 the headers are put there explicitly: https://github.com/apache/camel/blob/master/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvDataFormatTest.java
How could this very simple problem be solved without the need to iterate over the headers? I also experimented with different settings but all the same. The e.g delimiters have been considered I set but the headers not. Thanks for the responses also in advance.
I used Camel 2.16.1 like this:
final CsvDataFormat csvDataFormat = new CsvDataFormat();
csvDataFormat.setHeaderDisabled(false);
[...]
from("direct:TEST").routeId("TEST")
.setBody(constant("SELECT * FROM MYTABLE"))
.to("jdbc:myDataSource?readSize=100") // max 100 records
// .split(simple("${body}")) // split the list
// .streaming() // not to keep all messages in memory
.marshal(csvDataFormat)
.to("file:extract?fileName=TEST.csv");
[...]
EDIT 1
I have also tried to add the headers from the exchange.in. They are there available with the name "CamelJdbcColumnNames" in a HashSet. I added it to the csvDataFormat like this:
final CsvDataFormat csvDataFormat = new CsvDataFormat();
csvDataFormat.setHeaderDisabled(false);
[...]
from("direct:TEST").routeId("TEST")
.setBody(constant("SELECT * FROM MYTABLE"))
.to("jdbc:myDataSource?readSize=100") // max 100 records
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
headerNames = (HashSet)exchange.getIn().getHeader("CamelJdbcColumnNames");
System.out.println("#### Process headernames = " + new ArrayList<String>(headerNames).toString());
csvDataFormat.setHeader(new ArrayList<String>(headerNames));
}
})
.marshal(csvDataFormat)//.tracing()
.to("file:extract?fileName=TEST.csv");
The println() prints the column names but the cvs file generated does not.
EDIT2
I added the header names to the body as proposed in comment 1 like this:
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
Set<String> headerNames = (HashSet)exchange.getIn().getHeader("CamelJdbcColumnNames");
Map<String, String> nameMap = new LinkedHashMap<String, String>();
for (String name: headerNames){
nameMap.put(name, name);
}
List<Map> listWithHeaders = new ArrayList<Map>();
listWithHeaders.add(nameMap);
List<Map> records = exchange.getIn().getBody(List.class);
listWithHeaders.addAll(records);
exchange.getIn().setBody(listWithHeaders, List.class);
System.out.println("#### Process headernames = " + new ArrayList<String>(headerNames).toString());
csvDataFormat.setHeader(new ArrayList<String>(headerNames));
}
})
The proposal solved the problem and thank you for that but it means that CsvDataFormat is not really usable. The exchange body after the JDBC query contains an ArrayList from HashMaps containing one record of the table. The key of the HashMap is the name of the column and the value is the value. So setting the config value for the header output in CsvDataFormat should be more than enough to get the headers generated. Do you know a simpler solution or did I miss something in the configuration?
You take the data from a database with JDBC so you need to add the headers yourself first to the message body so its the first row. The resultset from the jdbc is just the data, not including headers.
I have done it by overriding the BindyCsvDataFormat and BindyCsvFactory
public class BindySplittedCsvDataFormat extends BindyCsvDataFormat {
private boolean marshallingfirslLot = false;
public BindySplittedCsvDataFormat() {
super();
}
public BindySplittedCsvDataFormat(Class<?> type) {
super(type);
}
#Override
public void marshal(Exchange exchange, Object body, OutputStream outputStream) throws Exception {
marshallingfirslLot = new Integer(0).equals(exchange.getProperty("CamelSplitIndex"));
super.marshal(exchange, body, outputStream);
}
#Override
protected BindyAbstractFactory createModelFactory(FormatFactory formatFactory) throws Exception {
BindySplittedCsvFactory bindyCsvFactory = new BindySplittedCsvFactory(getClassType(), this);
bindyCsvFactory.setFormatFactory(formatFactory);
return bindyCsvFactory;
}
protected boolean isMarshallingFirslLot() {
return marshallingfirslLot;
}
}
public class BindySplittedCsvFactory extends BindyCsvFactory {
private BindySplittedCsvDataFormat bindySplittedCsvDataFormat;
public BindySplittedCsvFactory(Class<?> type, BindySplittedCsvDataFormat bindySplittedCsvDataFormat) throws Exception {
super(type);
this.bindySplittedCsvDataFormat = bindySplittedCsvDataFormat;
}
#Override
public boolean getGenerateHeaderColumnNames() {
return super.getGenerateHeaderColumnNames() && bindySplittedCsvDataFormat.isMarshallingFirslLot();
}
}
My solution with spring xml (but I'd like to have an option in for extracting also the header on top:
Using spring xml
<multicast stopOnException="true">
<pipeline>
<log message="saving table ${headers.tablename} header to ${headers.CamelFileName}..."/>
<setBody>
<groovy>request.headers.get('CamelJdbcColumnNames').join(";") + "\n"</groovy>
</setBody>
<to uri="file:output"/>
</pipeline>
<pipeline>
<log message="saving table ${headers.tablename} rows to ${headers.CamelFileName}..."/>
<marshal>
<csv delimiter=";" headerDisabled="false" useMaps="true"/>
</marshal>
<to uri="file:output?fileExist=Append"/>
</pipeline>
</multicast>
http://www.redaelli.org/matteo-blog/2019/05/24/exporting-database-tables-to-csv-files-with-apache-camel/

How to avoid java.net.UnknownHostException while parsing HTML content to generate Pdf file using iText

I want to convert some HTML content into a PDF file. The problem I'm facing is that the HTML content has some <img> tags with absolute image urls. Hence the
HTMLWorker.parse()
method throws following exception in case there is no network connectivity.
ExceptionConverter: java.net.UnknownHostException: xyz.com
Is there a way to avoid this exception in such case and generate a pdf without any image?
I'm using iText-5.0.5 library.
You should implement your ImageProvider and when there is a problem retrieving the image just return null, like
public static class MyImageProvider implements ImageProvider {
public Image getImage(String src, Map<String, String> h, ChainedProperties cprops, DocListener doc) {
try {
return Image.getInstance(IMAGE_URL); //create IMAGE_URL from src parameter
} catch (IOException e) {
return null;
}
}
}
Then you should use the HTMLWorker with this provider
HashMap<String,Object> map = new HashMap<String, Object>();
map.put(HTMLWorker.IMG_PROVIDER, new MyImageProvider());
HTMLWorker.parseToList(new FileReader(HTML), null, map);

Getting and using remote JSON data

I'm working on a little app and using GWT to build it.
I just tried making a request to a remote server which will return a response as JSON.
I've tried using the overlay types concept but I couldn't get it working. I've been changing the code around so its a bit off from where the Google GWT tutorials left.
JavaScriptObject json;
public JavaScriptObject executeQuery(String query) {
String url = "http://api.domain.com?client_id=xxxx&query=";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
URL.encode(url + query));
try {
#SuppressWarnings("unused")
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
// violation, etc.)
}
public void onResponseReceived(Request request,
Response response) {
if (200 == response.getStatusCode()) {
// Process the response in response.getText()
json =parseJson(response.getText());
} else {
}
}
});
} catch (RequestException e) {
// Couldn't connect to server
}
return json;
}
public static native JavaScriptObject parseJson(String jsonStr) /*-{
return eval(jsonStr );
;
}-*/;
In the chrome's debugger I get umbrellaexception, unable to see the stack trace and GWT debugger dies with NoSuchMethodError... Any ideas, pointers?
You may have a look to GWT AutoBean framework.
AutoBean allow you to serialize and deserialize JSON string from and to Plain Old Java Object.
For me this framework became essential :
Code is cleaner than with JSNI objects (JavaScript Native Interface)
No dependancy with Framework not supported by Google (like RestyGWT)
You just define interfaces with getters and setters :
// Declare any bean-like interface with matching getters and setters,
// no base type is necessary
interface Person {
Address getAddress();
String getName();
void setName(String name):
void setAddress(Address a);
}
interface Address {
String getZipcode();
void setZipcode(String zipCode);
}
Later you can serialize or deserialize JSON String using a factory (See documentation) :
// (...)
String serializeToJson(Person person) {
// Retrieve the AutoBean controller
AutoBean<Person> bean = AutoBeanUtils.getAutoBean(person);
return AutoBeanCodex.encode(bean).getPayload();
}
Person deserializeFromJson(String json) {
AutoBean<Person> bean = AutoBeanCodex.decode(myFactory, Person.class, json);
return bean.as();
}
// (...)
First post on Stack Overflow (!) : I hope this help :)
Use JsonUtils#safeEval() to evaluate the JSON string instead of calling eval() directly.
More importantly, don't try to pass the result of an asynchronous call (like RequestBuilder#sendRequest() back to a caller using return - use a callback:
public void executeQuery(String query,
final AsyncCallback<JavaScriptObject> callback)
{
...
try {
builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable caught) {
callback.onFailure(caught);
}
public void onResponseReceived(Request request, Response response) {
if (Response.SC_OK == response.getStatusCode()) {
try {
callback.onSuccess(JsonUtils.safeEval(response.getText()));
} catch (IllegalArgumentException iax) {
callback.onFailure(iax);
}
} else {
// Better to use a typed exception here to indicate the specific
// cause of the failure.
callback.onFailure(new Exception("Bad return code."));
}
}
});
} catch (RequestException e) {
callback.onFailure(e);
}
}
Generally, the workflow you're describing consists of four steps:
Make the request
Receive the JSON text
Parse the JSON in JavaScript objects
Describe these JavaScript objects using an overlay type
It sounds like you've already got steps 1 and 2 working properly.
Parse the JSON
JSONParser.parseStrict will do nicely. You'll be returned a JSONValue object.
This will allow you to avoid using your custom native method and will also make sure that it prevents arbitrary code execution while parsing the JSON. If your JSON payload is trusted and you want raw speed, use JSONParser.parseLenient. In either case, you need not write your own parser method.
Let's say that you're expecting the following JSON:
{
"name": "Bob Jones",
"occupations": [
"Igloo renovations contractor",
"Cesium clock cleaner"
]
}
Since you know that the JSON describes an object, you can tell the JSONValue that you're expecting to get a JavaScriptObject.
String jsonText = makeRequestAndGetJsonText(); // assume you've already made request
JSONValue jsonValue = JSONParser.parseStrict(jsonText);
JSONObject jsonObject = jsonValue.isObject(); // assert that this is an object
if (jsonObject == null) {
// uh oh, it wasn't an object after
// do error handling here
throw new RuntimeException("JSON payload did not describe an object");
}
Describe as an overlay type
Now that you know that your JSON describes an object, you can get that object and describe it in terms of a JavaScript class. Say you have this overlay type:
class Person {
String getName() /*-{
return this.name;
}-*/;
JsArray getOccupations() /*-{
return this.occupations;
}-*/;
}
You can make your new JavaScript object conform to this Java class by doing a cast:
Person person = jsonObject.getJavaScriptObject().cast();
String name = person.getName(); // name is "Bob Jones"
Using eval is generally dangerous, and can result in all kinds of strange behavior, if the server returns invalid JSON (note, that it's necessary, that the JSON top element is an array, if you simply use eval(jsonStr)!). So I'd make the server return a very simple result like
[ "hello" ]
and see, if the error still occurs, or if you can get a better stack trace.
Note: I assume, that the server is reachable under the same URL + port + protocol as your GWT host page (otherwise, RequestBuilder wouldn't work anyway due to Same Origin Policy.)
You actually don't need to parse the JSON, you can use native JSNI objects (JavaScript Native Interface).
Here's an example I pulled from a recent project doing basically the same thing you're doing:
public class Person extends JavaScriptObject{
// Overlay types always have protected, zero argument constructors.
protected Person(){}
// JSNI methods to get stock data
public final native String getName() /*-{ return this.name; }-*/;
public final native String getOccupation() /*-{ return this.occupation; }-*/;
// Non-JSNI methods below
}
and then to retrieve it like so:
/**
* Convert the string of JSON into JavaScript object.
*
*/
private final native JsArray<Person> asArrayOfPollData(String json) /*-{
return eval(json);
}-*/;
private void retrievePeopleList(){
errorMsgLabel.setVisible(false);
String url = JSON_URL;
url = URL.encode(url);
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
try{
#SuppressWarnings("unused")
Request request = builder.sendRequest(null, new RequestCallback() {
#Override
public void onResponseReceived(Request req, Response resp) {
if(resp.getStatusCode() == 200){
JsArray<Person> jsonPeople = asArrayOfPeopleData(resp.getText());
populatePeopleTable(people);
}
else{
displayError("Couldn't retrieve JSON (" + resp.getStatusText() + ")");
}
}
#Override
public void onError(Request req, Throwable arg1) {
System.out.println("couldn't retrieve JSON");
displayError("Couldn't retrieve JSON");
}
});
} catch(RequestException e) {
System.out.println("couldn't retrieve JSON");
displayError("Couldn't retrieve JSON");
}
}
So essentially you're casting the response as an array of JSON Objects. Good stuff.
More info here: http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html