spring boot and thymeleaf for email only - spring-boot

I have a spring boot application that works correctly.
I need to add a functionality to send emails if certain actions happens.
I have added the thymeleaf code:
#Configuration
public class ThymeleafTemplateConfig {
#Bean
public SpringTemplateEngine springTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(htmlTemplateResolver());
return templateEngine;
}
#Bean
public FileTemplateResolver htmlTemplateResolver() {
FileTemplateResolver emailTemplateResolver = new FileTemplateResolver();
emailTemplateResolver.setPrefix(EMAILS_TEMPLATES_DIRECTORY);
//emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML");
emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
return emailTemplateResolver;
}
}
and below the code for handling the root url /
#Controller
public class IndexController {
#RequestMapping("/")
public String index() {
//return "main.xhtml";
return "operation.xhtml";
}
}
When I login to my site I redirect to "http://mysiteip/" and I have set as mapping that the "/ " is the operation.xhtml
so at the redirection or if I hit "http://mysiteip/" an error occurs.
ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - : - : - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "operation.xhtml", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "operation.xhtml", template might not exist or might not be accessible by any of the configured Template Resolvers
Why the thymeleaf tries to render the view? I want to use thymeleaf only for the email part (send emails with html body).

Related

Spring Boot - SOAP endpoint conflict with HandlerMapping / Dispatcher

I have a spring boot project with thymeleaft rest + soap.
I have a page that does:
Show the front end
+ Rest Requests
+ Soap requests.
The problem originates from "Soap" when I have to create the endpoint.
When I add this endpoint to the configuration:
#Bean
public ServletRegistrationBean<CXFServlet> dispatcherServlet() {
return new ServletRegistrationBean<CXFServlet>(new CXFServlet(), "/soap-api/*");
}
#Bean
#Primary
public DispatcherServletPath dispatcherServletPathProvider() {
return () -> "";
}
I am using a soap with apache cfx. The cfx configuration I use for my project is here
I have found a slightly unexpected error.
If I run this config inside my config I get all these problems:
config inside my config
I get an error of the type:
Error creating bean with name 'resourceHandlerMapping' defined in
class path resource
[org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.web.servlet.HandlerMapping]: Factory
method 'resourceHandlerMapping' threw exception; nested exception is
java.lang.IllegalStateException: No ServletContext set
I tried with #EnableWebMvc but I was getting a problem from:
Error creating bean with name 'resourceHandlerMapping' defined in
class path resource
[org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]
At this point I have tried: add the path for the thymeleaft
ClassLoaderTemplateResolver secondaryTemplateResolver = new ClassLoaderTemplateResolver();
But it does not work. I have also tried with basic mapping:
UrlBasedViewResolver resolve = new UrlBasedViewResolver();
resolve.setPrefix("templates/");
resolve.setSuffix(".html");
But it doesn't work either.
config outside my config
The strange thing is that if I set that configuration aside in another
java file, no problem arises. But when I access the page I get an
error:
ERROR 10788 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[...] :
Custom error page [/error] could not be dispatched correctly
So I thought mapping the traditional way like spring, in another java configuration does:
#Bean
public UrlBasedViewResolver viewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
//resolver.setPrefix("/WEB-INF/view/");
//resolver.setSuffix(".jsp");
// THYMELEAFT
resolver.setPrefix("templates/");
resolver.setSuffix(".html");
resolver.setViewClass(JstlView.class);
return resolver;
}
// RESOURCES
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/", "/resources/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
#Bean
public ResourceBundleThemeSource themeSource() {
ResourceBundleThemeSource themeSource
= new ResourceBundleThemeSource();
themeSource.setDefaultEncoding("UTF-8");
themeSource.setBasenamePrefix("themes.");
return themeSource;
}
It doesn't work either. I am currently at this point. Is there any solution for problem 2?
At this point I can successfully load the SOAP, but the other pages in
the browser show up as 404. and in the console I get: Custom error
page [/error] could not be dispatched correctly
From what I understand it makes a mess with the mapped routes
and I understand is that when I register a servlet "the previous or current" is lost and it is not mapped with the ServletRegistrationBean..
Is there any way to fix this? Do I have to map all the routes by hand with the ClassLoaderTemplateResolver or the UrlBasedViewResolver? I've tried it, but it throws the same error.
In case of changing while advancing "a different error comes out every time". That's why I'm looking for a way to directly solve the problem.
Note: I am using the apache.cxf plugin
I have found a way to make it work by declaring it in another way.
I think every day I'm understanding less how Spring/Spring Boot works. Is it time to return to JEE?
1 - It seems that if you leave the dispatcher empty (obviously it will fail with an empty arrow function) but Spring requires you to create a dispatcher when you declare ServletRegistrationBean. Thinking that: "that dispatcher "could" get the route automatically with an empty arrow function". But it doesn't.
// #Bean
// public ServletRegistrationBean<CXFServlet> dispatcherServlet() {
// return new ServletRegistrationBean<CXFServlet>(new CXFServlet(), "/soap-api/*");
// }
// #Bean
// #Primary
// public DispatcherServletPath dispatcherServletPathProvider() {
// return () -> "";
// }
2 - If I declare it a little differently, it is able to correctly detect the mappings
#Bean
public ServletRegistrationBean servletRegistrationBean() {
CXFServlet cxfServlet = new CXFServlet();
ServletRegistrationBean servletRegistration =
//new ServletRegistrationBean(cxfServlet, ServiceConstants.URL_PATH_SERVICES + "/*");
new ServletRegistrationBean(cxfServlet, "/soap-api/*");
return servletRegistration;
}
Problem source: here

How to test complete IntegrationFlow from MessagingGateway to ServiceActivator

In our Spring Boot project we have the following IntegrationFlow configuration
package our.configs;
#Configuration
#EnableIntegration
public class MessageChannelsConfiguration {
public static final String OUTBOUND_CHANNEL = "outboundChannel";
public static final String OUTBOUND_CHANNEL_GROUP_ID = "outboundMessageGroup";
#Bean
IntegrationFlow outboundSnapshotMessageChannel(ChannelMessageStore outboundChannelMessageStore,
OutboundFixMessageService outboundMessageService) {
return f -> f
.channel(c -> c.queue(
OUTBOUND_CHANNEL,
outboundChannelMessageStore,
OUTBOUND_CHANNEL_GROUP_ID))
.handle(outboundMessageService, "processOutboundMessage");
}
#Bean
OutboundMessageService outboundFixMessageService(ObjectMapper objectMapper){
return new OutboundMessageService(objectMapper);
}
#Bean
ChannelMessageStore outboundChannelMessageStore(#Qualifier("dataSource") DataSource dataSource,
ChannelMessageStoreQueryProvider channelMessageStoreQueryProvider) {
JdbcChannelMessageStore jdbcChannelMessageStore = new JdbcChannelMessageStore(dataSource);
jdbcChannelMessageStore.setChannelMessageStoreQueryProvider(channelMessageStoreQueryProvider);
jdbcChannelMessageStore.setRegion("TX_TIMEOUT");
return jdbcChannelMessageStore;
}
#Bean
#Profile({"test"})
ChannelMessageStoreQueryProvider jdbcChannelMessageStoreQueryProvider() {
return new H2ChannelMessageStoreQueryProvider();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller(TransactionManager transactionManager,
Initiator clientInitiator) {
return Pollers.fixedRate(500))
.maxMessagesPerPoll(1)
.advice(transactionInterceptor(transactionManager), new CheckSessionPollingAdvise(clientInitiator))
.get();
}
private TransactionInterceptor transactionInterceptor(TransactionManager transactionManager) {
return new TransactionInterceptorBuilder()
.transactionManager(transactionManager)
.propagation(Propagation.NESTED)
.build();
}
}
and the messaging Gateway which is defined as in a separate package then the above configuration
package our.businesslogic;
#MessagingGateway
public interface OutboundMessageGateway {
#Gateway(requestChannel = MessageChannelsConfiguration.OUTBOUND_CHANNEL)
void sendMarkerMessage(Object markerMessage,
#Header(ChannelMessageHeaders.RECIPIENT_ID) String institutionId,
#Header(ChannelMessageHeaders.MESSAGE_TYPE) ChannelMessageType channelMessageType);
#Gateway(requestChannel = MessageChannelsConfiguration.OUTBOUND_CHANNEL)
void sendOrderMessage(Order message,
#Header(ChannelMessageHeaders.RECIPIENT_ID) String institutionId,
#Header(ChannelMessageHeaders.MESSAGE_TYPE) ChannelMessageType channelMessageType);
}
I want to test the behavior of the complete flow including the persistence to the JdbcChannelMessageStore (and later also the transactional scenarios) with JUnit5.
E.g.
#Test
void whenSendTrancheMessage_givenPollingBlockedByAdvise_thenCorrectNumberOfMessagesOnQueue() {
//given
String recipientId = "Mocked-recipient";
List<Order> orders = Arrays.asList(
new Order(),
new Order(),
new Order(),
new Order()
);
//when
clientInitiator.stopConnection(); // Queue will not be read as long as
// there is no connection
orders.forEach(order->
outboundMessageGateway.sendOrderMessage(order,recipientId,ChannelMessageType.SNAPSHOT));
//then
Assertions.assertThat(outboundChannelMessageStore.messageGroupSize(FixMessageChannelsConfiguration.OUTBOUND_CHANNEL_GROUP_ID))
.isEqualTo(orders.size());
}
#Test
void whenSendTrancheMessage_givenPollingIsNotBlocked_thenMessagesAreReceivedByHandler() {
//some test code with mocked ServiceActivator
}
I have tried with two different ways
as an Integration test with #SpringBootTest
as a context specific JUnit test with #ContextConfiguration and #SpringIntegrationTest
In case of 1) my tests are working when called separately, but are failing with the following exception when they are run together with existing integration tests
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class p
ath resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation vi
a factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servl
et.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.h2.jdbc.JdbcSQLNonTransientConnection
Exception: Exception opening port "9092" (port may be in use), cause: "java.net.BindException: Address already in use: JVM_Bind" [900
61-199]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:483)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowir
eCapableBeanFactory.java:1336)
In case of 2) the following exception is thrown showing problems with the outboundMessageGateway
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
... ... ...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'outboundMessageGateway': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [our.businesslogic.OutboundMessageGateway]: Specified class is an interface
I would appreciate it very much if someone could help me to solve this issues.
Exception opening port "9092"
You probably need to add a #DirtiesContext along side with the mentioned #SpringBootTest.
Failed to instantiate [our.businesslogic.OutboundMessageGateway]
If you don't use Spring Boot in your test environment, you must be explicit #EnableIntegration and #IntegrationComponentScan must be there to Spring Integration infrastructure available.
See docs for more info: https://docs.spring.io/spring-integration/docs/current/reference/html/overview.html#configuration-enable-integration
Assertions.assertThat(outboundChannelMessageStore.messageGroupSize(FixMessageChannelsConfiguration.OUTBOUND_CHANNEL_GROUP_ID))
.isEqualTo(orders.size());
You can't check this if your handle(outboundMessageService, "processOutboundMessage") is not stopped. Since you have a poller for the queue channel, it is going to pull all the message from the store and lets that handler to process them. So, there is going to be nothing in the store at the moment you try to verify it (or some wrong unexpected number).

DelegatingDataSource Spring boot

I am trying to implement Spring Boot AOP for data-source pointcut - where before running any query I need to set client context in DB connection.
I was trying this approach of using DelegatingDataSource. But I am getting below error during server startup
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?
Please let me know DeletegatingDatasource for JNDI based DB lookup.
Edit 1: AOP - I tried to add pointcut execution(public * javax.sql.DataSource+.getConnection(..)). This works only when Spring datasource is used with username/password. Once i deploy in Jboss with JNDI I am getting WildFlyDataSource Proxy error. So, instead of AOP approach I thought of using DelegatingDatasource
// AOP Example
#Pointcut("execution(public * javax.sql.DataSource+.getConnection(..))")
void prepareConnectionPointcut() {
logger.debug("prepareConnectionPointcut");
}
#AfterReturning(pointcut = "prepareConnectionPointcut()", returning = "connection")
void afterPrepareConnection(Connection connection) {
// Set context in Connection - return same connection for query execution
}
But when i deploy this code in JBoss - I am getting WildFlyDataSource datasource bean creation error.
Error creating bean with name
'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration':
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'dataSource' defined in class path resource
[org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.class]:
Initialization of bean failed; nested exception is
org.springframework.aop.framework.AopConfigException: Could not
generate CGLIB subclass of class
org.jboss.as.connector.subsystems.datasources.WildFlyDataSource:
Common causes of this problem include using a final class or a
non-visible class; nested exception is
org.springframework.cglib.core.CodeGenerationException:
java.lang.NoClassDefFoundError-->org/jboss/as/connector/subsystems/datasources/WildFlyDataSource
I have also added proxyTargetClass flag during initialization
#EnableAspectJAutoProxy(proxyTargetClass = true)
Thanks #M.Deinum for recommendation of using BeanPostProcessor & Implement DelegatingDatasource for setting client info. Please find snippet below which i have implemented to accomplish this in Spring Boot which works well with JBoos JNDI based connection or Spring Boot URL Datasource connection.
#Component
public class MyBeanPostProcessor implements BeanPostProcessor {
private static Logger logger = LoggerFactory.getLogger(MyBeanPostProcessor.class);
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource) {
// Check DataSource bean initialization & enclose it with DelegatingDataSource
logger.debug("MyBeanPostProcessor:: postProcessAfterInitialization:: DataSource");
DataSource beanDs = (DataSource) bean;
return new MyDelegateDS(beanDs);
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource) {
logger.debug("MyBeanPostProcessor:: postProcessBeforeInitialization:: DataSource");
}
logger.debug("MyBeanPostProcessor:: postProcessBeforeInitialization:: " + beanName);
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
}
My implementation of DelegatingDataSource to handle each user request to set client context in DB connection session
public class MyDelegateDS extends DelegatingDataSource {
private static Logger logger = LoggerFactory.getLogger(MyDelegateDS.class);
public MyDelegateDS(DataSource delegate) {
super(delegate);
logger.debug("MyDelegateDS:: constructor");
}
#Override
public Connection getConnection() throws SQLException {
logger.debug("MyDelegateDS:: getConnection");
// To do this context only for user Request - to avoid this during Server initialization
if (RequestContextHolder.getRequestAttributes() != null
&& ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() != null) {
logger.debug("MyDelegateDS:: getConnection: valid user request");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
// Checking each user request & calling SP to set client context before invoking actual native query/SP
}
logger.debug("MyDelegateDS:: getConnection: Not User Request");
return super.getConnection();
}
}
Hope this is helpful for someone facing same problem

how to use custom custom classloader in springboot

How to load beans using custom classloader in spring boot
i need to encrypted the spring boot project.
at first i try using proguard, but fail because spring use lots of Annotations and it has DI .
then i try using custom classloader. i want to encrypt my class files first, and then use my custom classloader to load the encryped class file, then decrypted it。
here it is my demo: https://github.com/CaiBaoHong/boot-by-custom-loader
when it is starting up it fail:
ConfigServletWebServerApplicationContext :
Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'methodValidationPostProcessor' defined in class path resource
[org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.class]:
Unsatisfied dependency expressed through method 'methodValidationPostProcessor' parameter 0;
nested exception is
org.springframework.beans.factory.CannotLoadBeanClassException:
Error loading class [com.abc.bootbycustomloader.controller.UserController]
for bean with name 'userController' defined in file
[D:\code\boot-by-custom-loader\out\production\classes\com\abc\bootbycustomloader\controller\UserController.class]: problem with class file or dependent class;
nested exception is java.lang.LinkageError: loader (instance of com/abc/bootbycustomloader/loader/MyClassLoader):
attempted duplicate class definition for name:
"com/abc/bootbycustomloader/controller/UserController"
#SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
MyResourceLoader rLoader = new MyResourceLoader();
SpringApplication app = new SpringApplicationBuilder().build();
app.addPrimarySources(Arrays.asList(ServerApplication.class));
app.setResourceLoader(rLoader);
app.run(args);
}
}
#RestController
#RequestMapping("/user")
public class UserController {
#RequestMapping("/hello")
public String hello(){
System.out.println("hello hello hello...");
return "hello";
}
}
public class MyResourceLoader extends DefaultResourceLoader {
private ClassLoader cl = new MyClassLoader();
#Override
public Resource getResource(String location) {
System.out.println("getResource: "+location);
return super.getResource(location);
}
#Override
public ClassLoader getClassLoader() {
return cl;
}
}
public class MyClassLoader extends ClassLoader {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals("com.abc.bootbycustomloader.controller.UserController")) {
// assump that UserController is the encrypted class
// i need to load this encrypted class, and decrypted it!
System.out.println("!!!!!encrypted!!!!! : " + name);
// load the class from a special place, mock the decrypted processing
String path = "D:\\_clz\\UserController.class";
byte[] data = new byte[0];
try {
data = Files.readAllBytes(Paths.get(path));
} catch (IOException e) {
e.printStackTrace();
}
// mock decrypted processing success, return the decrypted class
Class<?> clz = defineClass(name, data, 0, data.length);
return clz;
} else {
// assump that other class is not encrypted class
// just load it as usual
return super.loadClass(name);
}
}
}

Cucumber Spring and class configuration

I'm struggling with Cucumber and Spring configuration.
I'm writing selenium framework using Page Object Pattern, with BrowserFactory.
When I use #ComponentScan, #Component and #Autowire annotations everything works fine, but when I want to create a bit more complicated bean with #Bean annotation (BrowserFactory which registers few browser drivers) in #Configuration class it does not work, during debug I'm getting nulls on every single variable I'm trying to Autowire.
I'm using Spring 4.2.4, all cucumber dependencies in version 1.2.4.
Config:
#Configuration
public class AppConfig {
#Bean
#Scope("cucumber-glue")
public BrowserFactory browserFactory() {
BrowserFactory browserFactory = new BrowserFactory();
browserFactory.registerBrowser(new ChromeBrowser());
browserFactory.registerBrowser(new FirefoxBrowser());
return browserFactory;
}
#Bean(name = "loginPage")
#Scope("cucumber-glue")
public LoginPage loginPage() throws Exception {
return new LoginPage();
}
#Bean(name = "login")
#Scope("cucumber-glue")
public Login login() {
return new Login();
}
}
POP:
public class LoginPage extends Page {
public LoginPage() throws Exception {
super();
}
...
}
Page:
public class Page {
#Autowired
private BrowserFactory browserFactory;
public Page() throws Exception{
...
}
}
Login:
public class Login {
#Autowired
private LoginPage loginPage;
public Login(){}
...
}
Steps:
#ContextConfiguration(classes = {AppConfig.class})
public class LoginSteps {
#Autowired
Login login;
public LoginSteps(){
}
#Given("^an? (admin|user) logs in$")
public void adminLogsIn(Login.User user) throws Exception {
World.currentScenario().write("Logging in as " + user + "\n");
login.as(user);
}
}
Error:
cucumber.runtime.CucumberException: Error creating bean with name 'LoginSteps': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: Login LoginSteps.login; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'login': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private LoginPage Login.loginPage; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginPage' defined in AppConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [LoginPage]: Factory method 'loginPage' threw exception; nested exception is java.lang.NullPointerException
And now for the fun part...
BrowserFactory in World class is properly Autowired!!
World:
public class World {
#Autowired
private BrowserFactory browserFactory;
...
}
So I'll answer my own question:)
Issue was that I was calling BrowserFactory inside of Page constructor.
Looks like this bean was not yet created and was causing NPEs.
In order to fix that I:
Added #Lazy annotation to configuration (all elements that use this configuration, both defined in that class and those which will be found by Scan will be created as Lazy)
Moved call to Browser Factory to #PostConstruct method
Two more things to increase readability of Spring config:
Added #ComponentScan to configuration
Classes with no constructor parameters are annotated with #Component and #Scope("cucumber-glue") annotation so bean creation can be removed from AppConfig.class

Resources