I have my tomcat datasource configured in XML as below:
<bean id="docDataSource"
class="org.apache.tomcat.jdbc.pool.DataSource"
destroy-method="close"
p:driverClassName="${doc.database.driver}"
p:url="${doc.database.url}"
p:username="${doc.database.user}"
p:password="${doc.database.password}"
p:validationQuery="select 1"
p:testOnBorrow="true"
p:minIdle="2"
p:maxIdle="4"
p:maxActive="6"
p:defaultTransactionIsolation="1">
</bean>
And my customDAO(groovy class) uses the above datasource as below
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component
import groovy.sql.Sql
import org.apache.log4j.Level
import javax.sql.DataSource
import java.util.Map
import java.util.Map.Entry
import java.util.ResourceBundle
#Component
public class customDao implements GroovyInterceptable {
#Autowired
private Services services
#Autowired
#Qualifier("docDataSource")
private DataSource dataSource
// implementation
}
I wanna switch my tomcat dataSource to a class file instead of XML. Can someone help me how to do this?
Here's the piece of code you can follow (with PostgreSQL, but it should more or less work the same):
import org.postgresql.ds.PGPoolingDataSource
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import javax.sql.DataSource
#Configuration
class PostgreSQLDatasourceConfiguration {
#Bean(name = 'dataSource')
DataSource ds(#Value('${DATABASE_URL}') String databaseFullUrl) {
assert databaseFullUrl, 'Database URL is required to start the application'
URI uri = new URI(databaseFullUrl)
def (_, dbUsername, dbPassword) = (uri.getUserInfo() =~ /(.*):(.*)/)[0]
(DataSource) new PGPoolingDataSource().tap {
url = "jdbc:postgresql://$uri.host:${uri.port}$uri.path"
password = dbPassword
user = dbUsername
}
}
}
Related
I'm constructing a Login with java, I've been following a tutorial but now I've encountered an issue. The app is supposed to be working and I should be able to try it on postman, but it is failing to start.
The issue is:
Description:
Field passwordEncoder in com.portfolio.anamorujaportfolio.Security.Controller.AuthController required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.portfolio.anamorujaportfolio.Security.Controller;
import com.portfolio.anamorujaportfolio.Security.Dto.JwtDto;
import com.portfolio.anamorujaportfolio.Security.Dto.LoginUsuario;
import com.portfolio.anamorujaportfolio.Security.Dto.NuevoUsuario;
import com.portfolio.anamorujaportfolio.Security.Entity.Rol;
import com.portfolio.anamorujaportfolio.Security.Entity.Usuario;
import com.portfolio.anamorujaportfolio.Security.Enums.RolNombre;
import com.portfolio.anamorujaportfolio.Security.Service.RolService;
import com.portfolio.anamorujaportfolio.Security.Service.UsuarioService;
import com.portfolio.anamorujaportfolio.Security.jwt.JwtProvider;
import java.util.HashSet;
import java.util.Set;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/auth")
#CrossOrigin
public class AuthController {
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
AuthenticationManager authenticationManager;
#Autowired
UsuarioService usuarioService;
#Autowired
RolService rolService;
#Autowired
JwtProvider jwtProvider;
#PostMapping("/nuevo")
public ResponseEntity<?> nuevo(#Valid #RequestBody NuevoUsuario nuevoUsuario, BindingResult bindingResult){
if(bindingResult.hasErrors())
return new ResponseEntity(new Mensaje("Campos mal puestos o email invalido"), HttpStatus.BAD_REQUEST);
if(usuarioService.existsByNombreUsuario(nuevoUsuario.getNombreUsuario()))
return new ResponseEntity(new Mensaje("Ese nombre de usuario ya existe"), HttpStatus.BAD_REQUEST);
if(usuarioService.existsByEmail(nuevoUsuario.getEmail()))
return new ResponseEntity(new Mensaje("Ese email ya existe"), HttpStatus.BAD_REQUEST);
Usuario usuario = new Usuario(nuevoUsuario.getNombre(), nuevoUsuario.getNombreUsuario(), nuevoUsuario.getEmail(), passwordEncoder.encode(nuevoUsuario.getPassword()));
Set<Rol>roles=new HashSet<>();
roles.add(rolService.getByRolNombre(RolNombre.ROLE_USER).get());
if(nuevoUsuario.getRoles().contains("admin"))
roles.add(rolService.getByRolNombre(RolNombre.ROLE_ADMIN).get());
usuario.setRoles(roles);
usuarioService.save(usuario);
return new ResponseEntity(new Mensaje("Usuario guardado"), HttpStatus.CREATED);
}
#PostMapping("/login")
public ResponseEntity<JwtDto> login(#Valid #RequestBody LoginUsuario loginUsuario, BindingResult bindingResult){
if(bindingResult.hasErrors())
return new ResponseEntity(new Mensaje("Campos mal puestos"), HttpStatus.BAD_REQUEST);
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginUsuario.getNombreUsuario(),loginUsuario.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtProvider.generateToken(authentication);
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
JwtDto jwtDto = new JwtDto(jwt, userDetails.getUsername(), userDetails.getAuthorities());
return new ResponseEntity(jwtDto, HttpStatus.OK);
}
}
It's my first go at Java, thanks in advance for the help!!
I've no idea, but I want it to run
The error says it all. Basically the application thinks that a configuration is missing which is unable to bind the PasswordEncoder.
Upon closer look, PasswordEncoder is part of the Spring package and the way AutoInjections work is (by default), spring only scans the classes from the root package and since its not part of the root it cant autowire the bean.
You can initialise it like this from a custom config class and annotate it as a Bean and then Autowire it..
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Please refer an implementation here
https://www.baeldung.com/spring-security-registration-password-encoding-bcrypt
Please refer to the documentation here
https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html#authentication-password-storage
Hope this helps!
I am developing a spring boot based application that supports internationalisation
My env:
Jdk11
Spring boot 2.7.0
Spring Security
Here is my web mvc conf
WebMvcConf.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
#Configuration
public class WebMvcConf implements WebMvcConfigurer {
public static final Locale idnLocale = Locale.forLanguageTag("id-ID");
#Bean
public LocaleResolver defaultLocaleResolver(){
var resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(idnLocale);
return resolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
var interceptor = new LocaleChangeInterceptor();
interceptor.setIgnoreInvalidLocale(false);
interceptor.setParamName("lang");
return interceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
And here is my security config:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;
#Configuration
#EnableWebSecurity
public class SecurityConf{
#Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()//enable cors
.and()
.csrf().disable()//disable csrf
.sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//stateless session (Rest)
.authorizeHttpRequests(authz->authz
.antMatchers(HttpMethod.GET,"/").permitAll()
.antMatchers(HttpMethod.POST,"/users/login","users/register","users/forgot-password").permitAll()
.antMatchers(HttpMethod.PATCH,"/users/password").permitAll()
.anyRequest().authenticated());//authorize any request except ignored endpoint above
return httpSecurity.build();
}
}
Here is the structure of the message properties:
Here is my application.yml content:
Here is the content of messages_en.properties:
rest.welcome.ok=Welcome to ABC Backend Service, have fun!!!
rest.users.login.ok=Successfully Logged in
#Error Message
rest.500.err=Internal Server Error
rest.422.err=Constraint Violation Error
rest.400.err=Invalid request body
rest.required-req-body-missing.err=Required request body is missing
Here is content of messages.properties:
rest.welcome.ok=Selamat Datang di Service ABC, selamat bersenang-senang!!!
rest.users.login.ok=Login sukses
#Error message
rest.500.err=Kesalahan Internal di sistem
rest.422.err=Error pelanggaran constraint
rest.400.err=Kesalahan pada request body
rest.required-req-body-missing.err=Request body (payload) yang dibutuhkan tidak ditemukan
Here is my test to test the message source:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.MessageSource;
import java.util.Locale;
#SpringBootTest
public class MessageSourceTest {
#Autowired
private MessageSource messageSource;
#BeforeEach
void init(){
}
#Test
public void message_provided_should_correctly_localized(){
String msgEnglish = messageSource.getMessage("rest.500.err",null, new Locale("en"));
String msgIndo = messageSource.getMessage("rest.500.err",null,Locale.forLanguageTag("id-ID"));
Assertions.assertEquals("Internal Server Error",msgEnglish);
Assertions.assertEquals("Kesalahan Internal di sistem",msgIndo);
}
}
And the test is pass:
But when I try to hit a mvc rest controller and add query parameter lang=en , I always got an error response saying Cannot change HTTP accept header - use a different locale resolution strategy
So I debug the LocaleChangeInterceptor and found that spring boot provide a wrong implementation of the LocaleResolver, they provide AcceptHeaderResolver, which what I want is the SessionLocaleResolver as I state it in my WebMvcConf class. See following picture:
Anyone knows what is wrong and how to fix it?any response will be appreciated
Had spent quite some time figuring out how to externalize ehCache 3 ehcache.xml outside of the jar file of an Spring 5 (Springboot 2.x) project. This is important so that ehcache settings could be tweaked without having to redeploy the project.
Just sharing a solution that worked using Java 8 in case anybody else faces this challenge:
package com.myproject.config;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import javax.cache.Caching;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configures ehCache.
*
* #author
*
*/
#Configuration
#EnableCaching
public class CacheConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheConfiguration.class);
#Value("${myproject.cache.ehcache.xml.fullpath:/dir/outside/of/project/config/ehcache.xml}")
private String ehcacheXmlFullPath;
#Bean
public CacheManager cacheManager() throws URISyntaxException {
// To get from the classpath: getClass().getResource("/ehcache.xml").toURI()
return new JCacheCacheManager(Caching.getCachingProvider().getCacheManager(Paths.get(ehcacheXmlFullPath).toUri(),
getClass().getClassLoader()));
}
}
I'm learning SpringMVC and maven these days with the book Spring in Action but i have a question now. The default request to "/" should be mapped to "home.jsp" but not. You can also see the same question described in the book forum.
https://forums.manning.com/posts/list/38046.page
Here are the codes:
package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
#Override
protected String[] getServletMappings(){
return new String[]{ "/" };
}
#Override
protected Class<?>[] getRootConfigClasses(){
return new Class<?>[]{ RootConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses(){
return new Class<?>[]{ WebConfig.class };
}
}
package spittr.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan("spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
package spittr.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#ComponentScan(basePackages={"spitter"}, excludeFilters={
#Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)
})
public class RootConfig {
}
package spittr.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class HomeController {
#RequestMapping(value="/", method=RequestMethod.GET)
public String home(){
return "home";
}
}
When i run this on tomcat 7.0, it should show home.jsp. However it still shows index.jsp.
-------------------- update -------------------------
The following test class indicates the controller class is right and this controller can response to the request "/" with home.jsp. So, where is wrong?
package spittr.web;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
public class HomeControllerTest {
#Test
public void testHomePage() throws Exception{
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
update or add in your web.xml
<welcome-file-list>home.jsp</welcome-file-list>
If you do not have web.xml you can generate by
Dynamic Web Project –> RightClick –> Java EE Tools –> Generate
Deployment Descriptor Stub.
Also you can do JSP redirect using JSTL libraries in index.jsp to redirect to home.jsp
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:redirect url="/home.jsp"/>
seems to be a path problem.
First check your path mapping in web.xml file for exact url-pattern for which you are redirecting to dispatcher servlet.Assuming that you have home.jsp views created under
/WEB-INF/views/
folder.
I had the same issue while working on Eclipse, Tomcat 9.0 on my Mac. I have spend hours to see (this small code) that where I was wrong.
However, I was able to make it run on Windows machine with Eclipse and Tomcat 8.5 and 9.0 .
I have the code hosted on GitHub at https://github.com/shortduck/ManningChapter5_SpringMVC
This is a Maven project and not Gradle. Also see the HomeController has the value as value = "/home", this is working as well as '/' will work too. If you have having value as '/' make sure index.jsp or any other "home" page is not on the root.
My next target to find out why is this code not working on Mac.
I have created a web app with Spring on the basis of Maven archetype for spring web app by kolorobot on GitHub
this archetype. Since I have not been developing with spring for several months, I need some help. This web app is using a HSQL db. I want to change the db, but i am not sure where to do it. Maybe somebody with more experience can help? This is the content of my persitence.properties file:
dataSource.driverClassName=org.hsqldb.jdbcDriver
dataSource.url=jdbc:hsqldb:mem:test
dataSource.username=sa
dataSource.password=
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.hbm2ddl.auto=create
In my web app there are already several config classes, that come along with the parent archetype.
This is my JPAConfig:
package org.stimpy.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import org.stimpy.Application;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackageClasses = Application.class)
class JpaConfig implements TransactionManagementConfigurer {
#Value("${dataSource.driverClassName}")
private String driver;
#Value("${dataSource.url}")
private String url;
#Value("${dataSource.username}")
private String username;
#Value("${dataSource.password}")
private String password;
#Value("${hibernate.dialect}")
private String dialect;
#Value("${hibernate.hbm2ddl.auto}")
private String hbm2ddlAuto;
#Bean
public DataSource configureDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("org.stimpy");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, dialect);
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, hbm2ddlAuto);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new JpaTransactionManager();
}
}
I know Spring is very configurable, but at the minute I am lost.
Update:
When I change the persitence properties to:
dataSource.driverClassName=org.h2.Driver
dataSource.url=jdbc:h2:mem:test
dataSource.username=sa
dataSource.password=
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=create
the maven package command fails. And several tests are failing. In the error output it said:
Table ACCOUNT was not found. Do I have to create the tables manually or does Spring do the job?
I solved the problem. I changed the properties to:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/mywebbapp
dataSource.username=root
dataSource.password=
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=create
hibernate.connection.url=jdbc:mysql://localhost:3306/mywebbapp
And then created a table named 'mywebabapp' in my local mysql database.
Now i am going to setup multiple environments (test, prod, dev). To let the tests run against the embedded H2-db like user Bobby Zohdy suggeseted.
Let's assume that you want to change the database to H2, then you only need the following changes (assuming of course you have added the H2 dependencies to the project):
dataSource.driverClassName=org.h2.Driver
dataSource.url=jdbc:h2:mem:test
hibernate.dialect=org.hibernate.dialect.H2Dialect
if you are going to use the in-memory database why not use spring embedded database like :
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>
or
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.type(H2).script("schema.sql").script("test-data.sql").build();
// do stuff against the db (EmbeddedDatabase extends javax.sql.DataSource)
db.shutdown()
and if you have multiple environments, you can use spring profile http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/