Could not read freemarker template - freemarker

My file is under
src/main/resources/freemarker/email_notification.txt
I am not able to read the freemaker file, that is email_notification.txt, which contain html file.
My Reference is from here: http://websystique.com/spring/spring-4-email-using-velocity-freemaker-template-library/
I have tried the velocity method but there is strikethrough in some of the words, thus i choose freemarker method instead.
#Transactional
#Service("EmailService")
public class EmailService{
#Autowired
JavaMailSender mailSender;
#Autowired
Configuration freemarkerConfiguration;
public void sendEmail(Map<String, Object> params) {
MimeMessagePreparator preparator = getMessagePreparator(params);
try {
mailSender.send(preparator);
System.out.println("Message has been sent.............................");
}
catch (MailException ex) {
System.err.println(ex.getMessage());
}
}
private MimeMessagePreparator getMessagePreparator(final Map<String, Object> params){
MimeMessagePreparator preparator = new MimeMessagePreparator() {
public void prepare(MimeMessage mimeMessage) throws Exception {
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject(params.get("trnmaster").toString());
helper.setFrom("XXXXX#gmail.com");
helper.setTo("XXXXXX#hotmail.com");
String text = geFreeMarkerTemplateContent(params);//Use Freemarker or Velocity
System.out.println("Template content : "+text);
helper.setText(text, true);
}
};
return preparator;
}
public String geFreeMarkerTemplateContent(Map<String, Object> model){
StringBuffer content = new StringBuffer();
try{
content.append(FreeMarkerTemplateUtils.processTemplateIntoString(
freemarkerConfiguration.getTemplate("/email_notification.txt"),model));
return content.toString();
}catch(Exception e){
System.out.println("Exception occured while processing fmtemplate:"+e.getMessage());
}
return "";
}
}
part of the config file
#Bean
public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration() {
FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
bean.setTemplateLoaderPath("classpath:/freemarker/");
return bean;
}

Where you configure FreeMarker, you should use: bean.setTemplateLoaderPath("classpath:/freemarker/");
Also that example is quite strange. Why's the file extension txt? It should be ftlh for a HTML template (was ftl in older projects). Also I would definitely overwrite FreeMarkerConfigurationFactoryBean.postProcessConfiguration and do the recommended setup according to https://freemarker.apache.org/docs/pgui_quickstart_createconfiguration.html, except that you must not call setDirectoryForTemplateLoading, and maybe setDefaultEncoding is already set by Spring as well. It's important to ensure that you get automatic HTML escaping (incompatibleImprovements set to 2.3.24 or higher and the ftlh file extension does that).

Related

How to send email with Freemarker template in Spring Boot app?

I am trying to send email with Freemarker template.
Code:
public String geContentFromTemplate(Map<String, Object> model) throws IOException, TemplateException {
StringWriter stringWriter = new StringWriter();
fmConfiguration.getTemplate("email-template.ftlh").process(model, stringWriter);
return stringWriter.getBuffer().toString();
}
public void sendEmailWithTemplate(String to, String subject, User user) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
try {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setSubject(subject);
mimeMessageHelper.setFrom(emailFrom);
mimeMessageHelper.setTo(to);
Map<String, Object> model = new HashMap<>();
model.put("firstName", user.getFirstName());
model.put("lastName", user.getLastName());
String content = geContentFromTemplate(model);
mimeMessageHelper.setText(content, true);
mailSender.send(mimeMessageHelper.getMimeMessage());
} catch (MessagingException | IOException | TemplateException e) {
e.printStackTrace();
}
}
Freemarker Bean:
#Bean
public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration() {
FreeMarkerConfigurationFactoryBean fmConfigFactoryBean = new FreeMarkerConfigurationFactoryBean();
fmConfigFactoryBean.setTemplateLoaderPath("classpath:templates/email-template.ftlh");
return fmConfigFactoryBean;
}
My template is located in Spring Boot application: resources/templates/email-template.ftlh
I receive this exception:
freemarker.template.TemplateNotFoundException: Template not found for name "email-template.ftlh". The name was interpreted by this TemplateLoader: org.springframework.ui.freemarker.SpringTemplateLoader#33cceeb3.
I fixed that by changing #Bean. I removed previous one and created another:
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer(){
freemarker.template.Configuration configuration = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_19);
TemplateLoader templateLoader = new ClassTemplateLoader(this.getClass(), "/templates/");
configuration.setTemplateLoader(templateLoader);
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
freeMarkerConfigurer.setConfiguration(configuration);
return freeMarkerConfigurer;
}
Also template loading implemented like that:
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("email-template.ftlh");
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);

Hibernate Anntotaion #Converter need to be configure before Spring Anntotation #propertySource

I am using #converter (Hibernate )to convert pojo in encrypted format which is from hibernate but key are placed in property file which would not be resolve by #propertySource (Spring annotation)
is there any way to manage bean creation seq in above case.
Please find the below code snippet for Converter, I had created another bean from encryption/decryption, but you can create config bean for properties and read properties from there.
#Component
#Converter
#Configurable
public class HashMapConverter implements AttributeConverter<Map<String, Object>, String> {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/*
* Define your application properties bean here and read the properties from
* there
*/
private static ConfigEncryptionKeyConverter configEncryptionKeyConverter;
#Autowired
public void initEncryptionKeyConverter(ConfigEncryptionKeyConverter configEncryptionKeyConverter) {
// Set your beans here.
HashMapConverter.configEncryptionKeyConverter = configEncryptionKeyConverter;
}
#Override
public String convertToDatabaseColumn(Map<String, Object> attribute) {
try {
return configEncryptionKeyConverter.convertToDatabaseColumn(OBJECT_MAPPER.writeValueAsString(attribute));
} catch (final JsonProcessingException e) {
throw new ApplicationErrorException(e.getLocalizedMessage());
}
}
#SuppressWarnings("unchecked")
#Override
public Map<String, Object> convertToEntityAttribute(String dbData) {
Map<String, Object> attribute = null;
if (dbData != null) {
try {
attribute = OBJECT_MAPPER.readValue(configEncryptionKeyConverter.convertToEntityAttribute(dbData),
Map.class);
} catch (final IOException e) {
throw new ApplicationErrorException(e.getLocalizedMessage());
}
}
return attribute;
}
}
Hope this will help.

How can I add information to a JAXBElement SOAP request?

I have a class generated with JAXB2 form a WSDL. The elements defined in the WSDL are NOT declared as XmlRootElement.
#Service
public class ProblemService extends WebServiceGatewaySupport {
public ProblemResponse addProblem(final Problem problem, final String aNumber) {
final String namespacePrefix = "soapenv";
final String action = "Problem";
final ObjectFactory factory = new ObjectFactory();
final JAXBElement<Problem> request = factory.createProblem(problem);
try {
StringResult result = new StringResult();
getMarshaller().marshal(request, result);
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace(System.err);
}
final WebServiceTemplate wst = this.getWebServiceTemplate();
#SuppressWarnings("unchecked")
final JAXBElement<ProblemResponse> response = (JAXBElement<ProblemResponse>) wst
.marshalSendAndReceive(abcConfiguration.getEndpoint(), request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(final WebServiceMessage message) {
try {
prepareSoapHeader(message, namespacePrefix, action);
final SaajSoapMessage ssMessage = (SaajSoapMessage) message;
final SOAPEnvelope envelope = ssMessage.getSaajMessage().getSOAPPart().getEnvelope();
envelope.getBody().setPrefix(namespacePrefix);
final NodeList nl = ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody().getChildNodes();
ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody().removeChild(nl.item(0));
final SOAPElement se = ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody()
.addBodyElement(new QName(action));
se.setPrefix(NAMESPACE_PREFIX_V2);
addUserAuthentification(se);
try {
StringResult result = new StringResult();
getAbcConfiguration().marshaller().marshal(request, result);
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace(System.err);
}
System.out.println();
} catch (SoapFaultClientException e) {
logger.error("Error on client side during marshalling of the SOAP request for {}.", action, e);
} catch (SOAPException e) {
logger.error("Error during marshalling of the SOAP request for {}.", action, e);
}
}
});
return response.getValue();
}
}
The generated StringResult looks quiet good but I need to replace some parts in the resulting XML (for instance the prefix) and I need to add some stuff into the SoapBody which are not part of the base class (Problem) before sending the SOAP request to the remote service.
Furthermore I want to modify the header part of the envelope...
How can I achieve this? My application is a SpringBoot application and in the configuration class being used in my service the un-/marshaller are defined this way:
#Bean
public Jaxb2Marshaller marshaller() {
final Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
//setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setContextPath(contextPath);
//marshaller.afterPropertiesSet();
marshaller.setMarshallerProperties(new HashMap<String, Object>() {{
put(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, true);
}});
return marshaller;
}
#Bean
public ProblemService problemService(final Jaxb2Marshaller marshaller) throws Exception {
final ProblemService client = new ProblemService();
client.setDefaultUri(this.endpoint);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
final HttpsUrlConnectionMessageSender msgSender = new HttpsUrlConnectionMessageSender();
client.setMessageSenders(new WebServiceMessageSender[] {msgSender, httpComponentsMessageSender()});
//client.setMessageSender(msgSender);
return client;
}
With this little piece of code I was able to add information to the SoapBody as demanded:
try {
getKpmConfiguration().marshaller().marshal(request, ssMessage.getPayloadResult());
ssMessage. writeTo(System.out);
} catch (/*JAXB*/Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

Embed object instead of collection in Spring HATEOAS

A very quick question ,to which there seems to be no easy answer.
Is it possible to put an object directly under the embedded resources using Spring HATEOAS? The desired output format in JSON should look like
{
...
_embedded: {
myObject: {
...
}
}
}
Using the code below, I always end up with a colletion for any resource I want to embed.
ArrayList<Resource<?>> embeddedContent = new ArrayList<>();
Resource<MyObject> myObjectResource = new Resource<MyObject>(new MyObject());
embeddedContent.add(myObjectResource );
Resources<Resource<?>> embeddedResources = new Resources<Resource<?>>(embeddedContent);
The embeddedResources are then put on a class, which is later mapped to a resource as well.
But for some reason, even though I'm not adding a collection to the embedded resources, the output still shows the myObject embedded resource as an array:
{
...
_embedded: {
myObject: [
{
...
}
]
}
}
The parameter enforceEmbeddedCollections in this constructor allow represent embedded arrays like a object.
public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider, boolean enforceEmbeddedCollections) {}
So, you should set HalHandlerInstantiator with value false.
There is a small example:
ObjectMapper halObjectMapper = new ObjectMapper();
halObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
halObjectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
halObjectMapper
.setHandlerInstantiator(new Jackson2HalModule.
HalHandlerInstantiator(new DefaultRelProvider(), null, false));
Jackson2HalModule jackson2HalModule = new Jackson2HalModule();
halObjectMapper.registerModule(jackson2HalModule);
try {
halObjectMapper.writeValueAsString(new Resources<Album>(Arrays.asList(new Album("1", "title", "1", 1))));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
Add the following snippet to one of your #Configuration classes. The code here looks similar to what can be found in org.springframework.hateoas.config.HypermediaSupportBeanDefinitionRegistrar. We're basically overwriting the HalHandlerInstantiator in the HAL-ObjectMapper where we pass false to the enforceEmbeddedCollections argument. This is a dirty hack, but currently there's no way to configure this aspect of the spring-hateoas machinery.
#Bean
BeanPostProcessor halModuleReconfigurer(BeanFactory beanFactory) {
return new BeanPostProcessor() {
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
adapter.setMessageConverters(reconfigureObjectMapper(adapter.getMessageConverters()));
}
if (bean instanceof AnnotationMethodHandlerAdapter) {
AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean;
List<HttpMessageConverter<?>> augmentedConverters = reconfigureObjectMapper(Arrays.asList(adapter
.getMessageConverters()));
adapter
.setMessageConverters(augmentedConverters.toArray(new HttpMessageConverter<?>[augmentedConverters.size()]));
}
if (bean instanceof RestTemplate) {
RestTemplate template = (RestTemplate) bean;
template.setMessageConverters(reconfigureObjectMapper(template.getMessageConverters()));
}
return bean;
}
private List<HttpMessageConverter<?>> reconfigureObjectMapper(final List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter halConverterCandidate = (MappingJackson2HttpMessageConverter) converter;
ObjectMapper objectMapper = halConverterCandidate.getObjectMapper();
if (Jackson2HalModule.isAlreadyRegisteredIn(objectMapper)) {
final CurieProvider curieProvider = Try.of(() -> beanFactory.getBean(CurieProvider.class)).getOrElse((CurieProvider) null);
final RelProvider relProvider = beanFactory.getBean("_relProvider", RelProvider.class);
final MessageSourceAccessor linkRelationMessageSource = beanFactory.getBean("linkRelationMessageSource", MessageSourceAccessor.class);
objectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider, linkRelationMessageSource, false));
}
}
}
return converters;
}
};
}

Spring Boot cache doesn't work

I trying to configure spring cache, but the method is executed still. I have the below code, and the civilStatus cache is not working. The method getCivilStatus() is executed always. Does Anybody know the reason?
#Configuration
#EnableCaching
public class ApplicationConfig {
#Autowired
private SocioDemographicInfoService socioDemographicInfo;
#Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("civilStatus");
return cacheManager;
}
}
#Service
public class SocioDemographicInfoService {
#Cacheable(value="civilStatus")
public Map<String, String> getCivilStatus(){
log.info("Retrieving civilStatus");
Map<String, String> civilStatus = new HashMap<String, String>();
BufferedReader br = null;
String line = "";
String cvsSplitBy = ",";
try {
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("CatalogoEstadoCivil.csv").getFile());
br = new BufferedReader(new FileReader(file));
while ((line = br.readLine()) != null) {
String[] cod = line.split(cvsSplitBy);
civilStatus.put(cod[0].trim(), cod[1]);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return civilStatus;
}
}
}
I believe you are using spring boot and setting up a server using a class something like this (given below). Add EnableCaching annotation on the same class and define CacheManager as given below, instead of a separate configuration class. That will make sure caching is enabled before your class get initialized.
#Configuration
#EnableAutoConfiguration
#ComponentScan
#EnableCaching
#PropertySource(ignoreResourceNotFound = true, value = {"classpath:application.properties"})
#ImportResource(value = { "classpath*:spring/*.xml" })
public class MyBootServer{
public static void main(String args[]){
ApplicationContext ctx = SpringApplication.run(MyBootServer.class, args);
}
#Bean(name="cacheManager")
public CacheManager getCacheManager() {
...// Your code
}
}
Nothing wrong in your over all code. I tested your configuration in my spring boot sample code and it works
You don't need the AOP and caching complexity your usecase is a lot simpler. Just create a method that loads the file at startup and let your getCivilStatus return that map. A lot simpler.
#Service
public class SocioDemographicInfoService implements ResourceLoaderAware {
private final Map<String, String> civilStatus = new HashMap<String, String>();
private ResourceLoader loader;
#PostConstruct
public void init() {
log.info("Retrieving civilStatus");
Map<String, String> civilStatus = new HashMap<String, String>();
BufferedReader br = null;
String line = "";
String cvsSplitBy = ",";
Resource input = loader.getResource("classpath:CatalogoEstadoCivil.csv"));
if (input.isReadable() ) {
File file = input.getFile();
br = new BufferedReader(new FileReader(file));
try {
while ((line = br.readLine()) != null) {
String[] cod = line.split(cvsSplitBy);
civilStatus.put(cod[0].trim(), cod[1]);
}
} catch (IOException e) {
logger.error("Error reading file", e_;
} finally {
if (br != null) {
try { br.close() } catch( IOException e) {}
}
}
}
}
public Map<String, String> getCivilStatus() {
return this.civilStatus;
}
public void setResourceLoader(ResourceLoader loader) {
this.loader=loader;
}
}
Something like this should work. It loads your after the bean is constructed (this code can probably be optimized by using something like commons-io). Note I used Springs ResourceLoader to load the file.

Resources