To improve my skills I've decided to grab an Udemy course about Spring framework. During this course there is a task to create logging into app using JDBC and C3P0.
I've followed all course details and instructor tips. After I deploy my app on Tomcat it is running, but I am unable to log in using the data that has been put in DB. My goal is to log into the app, but for now I am unable to make it. I have even dowloaded solution code, but even after firing it I am unable to log into the further app part.
Whole code structure is good (at least i do believe it). Below I have placed screen and code of most important classes and files.
I have tried to remove {noop} from the DB, but without any result. I have also tried to use an empty line as a password - without any success. When I was doing logging with auth from memory everything was fine, but when i try to parse it from DB i am unable to log in ( there are no error logs, I just got information from app that user and password are wrong.
DemoAppConfig.java
package com.luv2code.springsecurity.demo.config;
import java.beans.PropertyVetoException;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import com.mchange.v2.c3p0.ComboPooledDataSource;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="com.luv2code.springsecurity.demo")
#PropertySource("classpath:persistence-mysql.properties")
public class DemoAppConfig {
// set up variable to hold the properties
#Autowired
private Environment env;
// set up a logger for diagnostics
private Logger logger = Logger.getLogger(getClass().getName());
// define a bean for ViewResolver
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
// define a bean for our security datasource
#Bean
public DataSource securityDataSource() {
// create connection pool
ComboPooledDataSource securityDataSource
= new ComboPooledDataSource();
// set the jdbc driver class
try {
securityDataSource.setDriverClass(env.getProperty("jdbc.driver"));
} catch (PropertyVetoException exc) {
throw new RuntimeException(exc);
}
// log the connection props
// for sanity's sake, log this info
// just to make sure we are REALLY reading data from properties file
logger.info(">>> jdbc.url=" + env.getProperty("jdbc.url"));
logger.info(">>> jdbc.user=" + env.getProperty("jdbc.user"));
// set database connection props
securityDataSource.setJdbcUrl(env.getProperty("jdbc.url"));
securityDataSource.setUser(env.getProperty("jdbc.user"));
securityDataSource.setPassword(env.getProperty("jdbc.password"));
// set connection pool props
securityDataSource.setInitialPoolSize(
getIntProperty("connection.pool.initialPoolSize"));
securityDataSource.setMinPoolSize(
getIntProperty("connection.pool.minPoolSize"));
securityDataSource.setMaxPoolSize(
getIntProperty("connection.pool.maxPoolSize"));
securityDataSource.setMaxIdleTime(
getIntProperty("connection.pool.maxIdleTime"));
return securityDataSource;
}
// need a helper method
// read environment property and convert to int
private int getIntProperty(String propName) {
String propVal = env.getProperty(propName);
// now convert to int
int intPropVal = Integer.parseInt(propVal);
return intPropVal;
}
}
DemoSecurityConfig.java
package com.luv2code.springsecurity.demo.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.User.UserBuilder;
#Configuration
#EnableWebSecurity
public class DemoSecurityConfig extends WebSecurityConfigurerAdapter {
// add a reference to our security data source
#Autowired
private DataSource securityDataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// use jdbc authentication ... oh yeah!!!
auth.jdbcAuthentication().dataSource(securityDataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasRole("EMPLOYEE")
.antMatchers("/leaders/**").hasRole("MANAGER")
.antMatchers("/systems/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/showMyLoginPage")
.loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
}
MySpringMvcDispatcherServletInitializer.java
package com.luv2code.springsecurity.demo.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MySpringMvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { DemoAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
persistence-mysql.properties
#
# JDBC connection properties
#
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_security_demo_plaintext?useSSL=false
jdbc.user=hbstudent
jdbc.password=hbstudent
#
# Connection pool properties
#
connection.pool.initialPoolSize=5
connection.pool.minPoolSize=5
connection.pool.maxPoolSize=20
connection.pool.maxIdleTime=3000
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.luv2code</groupId>
<artifactId>spring-security-demo</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>spring-security-demo</name>
<properties>
<springframework.version>5.0.2.RELEASE</springframework.version>
<springsecurity.version>5.0.0.RELEASE</springsecurity.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- Spring Security -->
<!-- spring-security-web and spring-security-config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<!-- Add Spring Security Taglibs support -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<!-- Add MySQL and C3P0 support -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- Servlet, JSP and JSTL support -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- TO DO: Add support for Maven WAR Plugin -->
<build>
<finalName>spring-security-demo</finalName>
<pluginManagement>
<plugins>
<plugin>
<!-- Add Maven coordinates (GAV) for: maven-war-plugin -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
SQL code responsible for generating DB test data
DROP DATABASE IF EXISTS `spring_security_demo_plaintext`;
CREATE DATABASE IF NOT EXISTS `spring_security_demo_plaintext`;
USE `spring_security_demo_plaintext`;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
`password` varchar(50) NOT NULL,
`enabled` tinyint(1) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Inserting data for table `users`
--
INSERT INTO `users`
VALUES
('john','{noop}test123',1),
('mary','{noop}test123',1),
('susan','{noop}test123',1);
--
-- Table structure for table `authorities`
--
DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
`username` varchar(50) NOT NULL,
`authority` varchar(50) NOT NULL,
UNIQUE KEY `authorities_idx_1` (`username`,`authority`),
CONSTRAINT `authorities_ibfk_1` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Inserting data for table `authorities`
--
INSERT INTO `authorities`
VALUES
('john','ROLE_EMPLOYEE'),
('mary','ROLE_EMPLOYEE'),
('mary','ROLE_MANAGER'),
('susan','ROLE_EMPLOYEE'),
('susan','ROLE_ADMIN');
Type it as below:
username = "john"
password = "{noop}test123"
yes, exactly same password shown in the database, need to include the {noop} also for the password.
Related
I'm trying to build a Backend Service project using the example from the site
Using Spring Cloud Gateway with OAuth 2.0 Patterns
Here is the repository itself
backend
Added dependencies
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ru.test.gw.oauth.resource</groupId>
<artifactId>backresource</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>backresource</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>14</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
I moved the properties from the quotes-application.properties file to this project
server.port=11002
# Resource server settings
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:8484/auth/realms/demo/protocol/openid-connect/token/introspect
spring.security.oauth2.resourceserver.opaquetoken.client-id=gateway
spring.security.oauth2.resourceserver.opaquetoken.client-secret=dfdslksfkljweewrfsd
Added a class
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import reactor.core.publisher.Mono;
// Custom ReactiveTokenIntrospector to map realm roles into Spring GrantedAuthorities
public class KeycloakReactiveTokenInstrospector implements ReactiveOpaqueTokenIntrospector {
private final ReactiveOpaqueTokenIntrospector delegate;
public KeycloakReactiveTokenInstrospector(ReactiveOpaqueTokenIntrospector delegate) {
this.delegate = delegate;
}
#Override
public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {
return delegate.introspect(token)
.map( this::mapPrincipal);
}
protected OAuth2AuthenticatedPrincipal mapPrincipal(OAuth2AuthenticatedPrincipal principal) {
return new DefaultOAuth2AuthenticatedPrincipal(
principal.getName(),
principal.getAttributes(),
extractAuthorities(principal));
}
protected Collection<GrantedAuthority> extractAuthorities(OAuth2AuthenticatedPrincipal principal) {
//
Map<String,List<String>> realm_access = principal.getAttribute("realm_access");
List<String> roles = realm_access.getOrDefault("roles", Collections.emptyList());
List<GrantedAuthority> rolesAuthorities = roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
Set<GrantedAuthority> allAuthorities = new HashSet<>();
allAuthorities.addAll(principal.getAuthorities());
allAuthorities.addAll(rolesAuthorities);
return allAuthorities;
}
}
And the main class of the project
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import ru.test.gw.oauth.resource.backresource.security.KeycloakReactiveTokenInstrospector;
#SpringBootApplication
//#PropertySource("classpath:quotes-application.properties")
#EnableWebFluxSecurity
public class BackresourceApplication {
public static void main(String[] args) {
SpringApplication.run(BackresourceApplication.class, args);
}
#Bean
public SpringOpaqueTokenIntrospector keycloakIntrospector(OAuth2ResourceServerProperties props) {
NimbusReactiveOpaqueTokenIntrospector delegate = new NimbusReactiveOpaqueTokenIntrospector(
props.getOpaquetoken().getIntrospectionUri(),
props.getOpaquetoken().getClientId(),
props.getOpaquetoken().getClientSecret());
return new KeycloakReactiveTokenInstrospector(delegate);
}
}
And in this class I get an error on SpringOpaqueTokenIntrospector, writes that it is not defined. Although all the imports completely coincide with the training example.
If I add a dependency that the IDE tells me to
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
, then I get an error
Type mismatch: cannot convert from KeycloakReactiveTokenInstrospector to SpringOpaqueTokenIntrospector
What's the problem here? Is there some kind of dependency missing?
I completely repeated the structure of the project from the training material.
So far, I would like to build a project without errors.
Marcus is right in his comment, your keycloakIntrospector #Bean type should be ReactiveOpaqueTokenIntrospector (and not SpringOpaqueTokenIntrospector as declared in your conf)
Few facts:
SpringReactiveOpaqueTokenIntrospector is a ReactiveOpaqueTokenIntrospector but SpringOpaqueTokenIntrospector isn't
your KeycloakReactiveTokenInstrospector is (implements) a ReactiveOpaqueTokenIntrospector too but is neither a SpringReactiveOpaqueTokenIntrospector, SpringOpaqueTokenIntrospector nor OpaqueTokenIntrospector
Side notes
Introspection VS JWT decoding
Keycloak issues JWTs. JWT decoding is far more efficient than introspection: resource-server needs to fetch public-key only once from authorization-server to validate all incoming JWTs when introspection requires to submit access-token to authorization-server for each and every incoming request.
Also, you might not be able to implement multi-tenant scenarios with introspection: how to figure out by which issuer (Keycloak instance or realm) an opaque token was emitted? => you would have to "try" introspection on each issuer until one responds positively :/
Overriding introspector VS providing an authentication converter
If you switch to spring-security 5.8 or higher, customizing introspection is easier: you don't have to override the all introspector but can just provide a ReactiveOpaqueTokenAuthenticationConverter bean instead:
http.oauth2ResourceServer().opaqueToken().authenticationConverter(
(String introspectedToken, OAuth2AuthenticatedPrincipal authenticatedPrincipal) ->
new BearerTokenAuthentication(...));
This bean is called after introspection was successfuly completed (and token attributes retrieved) but before Authentication is instanciated and put in security-context which allows you to just map authorities from any attribute you like or completely switch the authentication implementation.
Simplifying your resource-server configuration
I host a set of libs to ease OAuth2 resource-server testing and configuration. There are various spring-boot starters depending on introspection or JWT decoding is used into servlet or reactive apps.
According to your case (reactive app with introspection), you should have a look at this sample with BearerTokenAuthentication and this other one with a custom authentication.
Configuration can be as simple as:
#EnableReactiveMethodSecurity
#Configuration
public class SecurityConfig {
}
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://localhost:8443/realms/master/protocol/openid-connect/token/introspect
spring.security.oauth2.resourceserver.opaquetoken.client-id=spring-addons-confidential
spring.security.oauth2.resourceserver.opaquetoken.client-secret=change-me
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/master
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.spring-addons-public.roles,resource_access.spring-addons-confidential.roles
# this is probably too permissive, addapt to your needs
com.c4-soft.springaddons.security.cors[0]=/**
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-introspecting-resource-server</artifactId>
<version>6.0.3</version><!-- warning, this version goes with spring-boot 3.0.0-RC1 -->
</dependency>
I am learning Spring Boot and in my first set up I got this problem:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured
I looked up solutions on the web and did find some, including those from Stackoverflow, but none of them works.
My simple code:
#SpringBootApplication
#RestController
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
#RequestMapping(value = "/")
public String response(){
return "You made it!";
}
}
My POM:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
My application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/hrdb?autoReconnect=true
spring.datasource.username=root
spring.datasource.password=passw0rd
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
I also created a file as the following:
#Configuration
public class DBConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/hr?autoReconnect=true");
dataSource.setUsername("root");
dataSource.setPassword("passw0rd");
return dataSource;
}
}
I got the mentioned error either in Unit Test or starting the service. The only way I find out to make it work is to disable data source auto config:
#SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
Need your help. Thanks.
Check your DB config - it seems to overwrite Spring's default data source, but the url is different than in application.properties: "jdbc:mysql://localhost:3306/hr?autoReconnect=true"
In the properties you have:
"jdbc:mysql://localhost:3306/hrdb?autoReconnect=true"
"hr" vs "hrdb".
I would get rid of this class altogether and instead declare the relevant JDBC driver in your pom.xml and let Spring take care of the connection.
I have a spring-boot application where I am trying to configure phoenix DataSource but getting "no suitable Driver" found error.
#Bean(name="phoenixDataSource")
#DependsOn(value = "placeholderConfigurer")
public DataSource phoenixDataSource() {
SimpleDriverDataSource phoenixDataSource = new SimpleDriverDataSource();
phoenixDataSource.setUrl( "jdbc:phoenix:localhost" );
try {
Class<?> driverClass = this.getClass().getClassLoader().loadClass("org.apache.phoenix.jdbc.PhoenixDriver");
phoenixDataSource.setDriverClass((Class<? extends Driver>) driverClass);
} catch( ClassNotFoundException e ) {
// TODO Auto-generated catch block
e.printStackTrace();
}
);
return phoenixDataSource;
}
#Bean(name = "phoenixJdbcTemplate")
public JdbcTemplate phoenixJdbcTemplate(#Qualifier("phoenixDataSource") DataSource ds) {
return new JdbcTemplate(ds);
}
First step you need to find wether you have access to connect or not
Connect to sqline using /usr/hdp/current/phoenix-client/bin/sqlline.py
/usr/hdp/current/phoenix-client/bin/sqlline.py <Zoo-keeper-url>:2181:/hbase-unsecure
If your Habse is not set unsecure so you need to find wether its Kerberos or HBase protected by Ranger authorization ,you can find required information in your logs.
Now you have following three options to connect
Zookeper URL with non secure
"jdbc:phoenix:<Zookeeper_host_name> :<port_number> : /hbase-unsecure"); //With No password
Zookeper URL with secure
"jdbc:phoenix:<Zookeeper_host_name>:<port_number>:<secured_Zookeeper_node>:<user_name> "
With URL
jdbc:phoenix:thin:url=<scheme>://<server-hostname>:<port>;authentication=vaquarkhan
Default zookeeper port =2181.
following code you can use for setup connection make sure you already added dependancy into POM file
POM :
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8.0_05</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-core</artifactId>
<version>4.7.0-HBase-1.1</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>sqlline</groupId>
<artifactId>sqlline</artifactId>
<version>1.1.9</version>
</dependency>
JavaCode:
package com.khan.vaquar.config;
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.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
/**
* Database Configures for Phoenix database.
*/
#Configuration
public class DBConfig {
#Bean
public DataSource dataSource() {
return new SimpleDriverDataSource(new org.apache.phoenix.jdbc.PhoenixDriver(),
"jdbc:phoenix:<Zookeeper-URL> :<PORT_NO> : /hbase-unsecure");
}
#Bean
public NamedParameterJdbcTemplate databasePhoenixJdbcTemplate() {
JdbcTemplate template = new JdbcTemplate(this.dataSource());
template.setQueryTimeout("1500");
return new NamedParameterJdbcTemplate(template);
}
}
Inside your repo use it for connection
#Autowired
private NamedParameterJdbcTemplate databasePhoenixJdbcTemplate;
Few useful links :
https://phoenix.apache.org/server.html
https://community.cloudera.com/t5/Support-Questions/How-to-pass-user-with-Phoenix-url/td-p/96707
https://community.cloudera.com/t5/Community-Articles/Phoenix-JDBC-Client-Setup/ta-p/244284
https://community.cloudera.com/t5/Support-Questions/SQuirreL-on-phoenix-Sandbox/m-p/153362
https://community.cloudera.com/t5/Community-Articles/Phoenix-Part-4-working-with-Ranger/ta-p/249174
There is two kind of drivers Thin and Thick.
Your code is using the thick driver.
so, you have to add the phoenix-core jar file to your classpath.
I'm using hdp 3.0.1.0-187 phoenix server.
In my gradle configuration is below.
implementation('org.apache.phoenix:phoenix-core:5.0.0-HBase-2.0')
I am trying to deploy the spring boot application with built in Tomcat Server. And, I am getting the below error before even the application gets started.
Field error in object 'spring.datasource' on field 'type': rejected value [org.apache.tomcat.dbcp.dbcp2.BasicDataSource]; codes [typeMismatch.spring.datasource.type,typeMismatch.type,typeMismatch.java.lang.Class,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [spring.datasource.type,type]; arguments []; default message [type]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Class' for property 'type'; nested exception is java.lang.IllegalArgumentException: Cannot find class [org.apache.tomcat.dbcp.dbcp2.BasicDataSource]] - o.s.b.c.e.AnnotationConfigEmbeddedWebApplicationContext
2017-11-26 22:18:27:489 - INFO - Correlation-Id = - AppName = - Server-IP = - RequestorApp = - RequestorIp = - UserId = - Total-Time = -
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled. - o.s.b.a.l.AutoConfigurationReportLoggingInitializer
2017-11-26 22:18:27:492 - ERROR - Correlation-Id = - AppName = - Server-IP = - RequestorApp = - RequestorIp = - UserId = - Total-Time = -
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.autoconfigure.jdbc.DataSourceProperties#466317f failed:
Property: spring.datasource.type
Value: org.apache.tomcat.dbcp.dbcp2.BasicDataSource
Reason: Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Class' for property 'type'; nested exception is java.lang.IllegalArgumentException: Cannot find class [org.apache.tomcat.dbcp.dbcp2.BasicDataSource]
Action:
Update your application's configuration
- o.s.b.d.LoggingFailureAnalysisReporter
Given below is my pom.xml file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cnanational.productservice</groupId>
<artifactId>product-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>product-service</name>
<description>Product Service</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss z</maven.build.timestamp.format>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.cnanational.servicecommon</groupId>
<artifactId>service-common</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>8.5.4</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>maven-releases</id>
<name>CNA National Release Artifacts</name>
<url>https://nexus.cnanational.net/repository/maven-releases/</url>
</repository>
<repository>
<id>maven-snapshots</id>
<name>CNA National Snapshot Artifacts</name>
<url>https://nexus.cnanational.net/repository/maven-snapshots/</url>
</repository>
<repository>
<id>maven-central</id>
<name>CNA National Central</name>
<url>https://nexus.cnanational.net/repository/maven-central/</url>
</repository>
</repositories>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<packagingExcludes>
**/product-service/
</packagingExcludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Given below is the spring-boot yml file.
security:
user:
name: qcSa
password: qcPassword
server:
port: 8100
context-path: /product-service
endpoints:
jmx:
domain: com.cnanational.productservice
management:
context-path: /${common.application-version}/actuator
spring:
application:
name: product-service
datasource:
# jndi-name: jdbc/web_base_user
url: jdbc:oracle:thin:#devrac-scan.cnanational.net:1521/d02wpa.cnanational.net
username: web_base_user
password: ubew6130
type: org.apache.tomcat.dbcp.dbcp2.BasicDataSource
product-service:
paths:
product-attributes: '/dealerProductAttributes'
product-surcharges: '/surcharges'
product-products: '/products'
deductible-descriptions:
S: Standard
D: Disappearing
sales-ws:
validation-rules-endpoint:
endpoint: https://dev03tc.cnanational.com/sales-ws/v1/uiGlobalSettings/search?appName=SalesRatingTool
user: SVC_DEV_CORS
password: 113501*3Q3E105^9^z7b
sql-properties:
product-procedure-no-rates-error-code: 20000
controlobj-sql: >
begin begin dbms_session.set_role(controlobj.getdisableddbroles);
exception when others then null;
end;
controlobj.web_set_user('%s', 'N','%s');
end;
controlobj-clear-errors-sql: >
call errorobj.clearerrormessage()
controlobj-query-errors-sql: >
select error_message from error_messagev1
agent-sales-rating-id-seq-sql:
select agent_sales_rating_id_seq.nextval from dual
rate-insertion-sql: >
insert into agent_sales_rating_parms
(agent_sales_rating_id, parm_name, parm_value1, parm_value2, parm_value3)
values
(:agent_sales_rating_id, :parm_name, :parm_value1, :parm_value2, :parm_value3)
rate-procedure-name: ins_agent_sales_rating_outputs
rate-params-query-sql: >
select
agent_sales_rating_id,
parm_name,
parm_value1,
parm_value2,
parm_value3
from
agent_sales_rating_parms
where
agent_sales_rating_id = :agent_sales_rating_id
and parm_name in (:param_names)
rate-query-sql: >
select
listagg(model_description,',') WITHIN GROUP ( order by model_description) as model
,vehicle_class
,term_months
,term_miles
, series_deductibles_id
, deduct_option
, deduct_amt
, retail_price
, dealer_cost
from
agent_sales_rating_outputs
where
agent_sales_rating_id = :agent_sales_rating_id
group by
vehicle_class
, term_months
, term_miles
, series_deductibles_id
, deduct_option
, deduct_amt
, retail_price
, dealer_cost
order by
vehicle_class desc
, series_deductibles_id
, deduct_option
, deduct_amt
, term_months
, term_miles
common:
application-version: v1
message-automation:
app-name: product-service
heavyweight: false
fallback-message: 'A system error has occurred'
message-ids:
common-exception-handler: 600000
common-error-controller: 600003
required-headers-filter: 600012
too-many-sort-arguments: 600011
invalid-sort-field: 600010
rest:
paths:
build-info: /buildInfo
remote-user-header-required-urls:
- /v1/**
headers:
remote-user: oam_remote_user
correlation-id: Correlation-Id
requestor-app: REQUESTOR_APP
health:
dependencies:
-
name: 'Oracle Datasource'
dependency-type: DB
Below is the context.xml file which has the beans information. And the Java class below has the application properties.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="timeElapsedAspect" class="com.cnanational.servicecommon.aop.TimeElapsedAspect"></bean> <aop:config> <aop:aspect id="timeElapsedAspect" ref="timeElapsedAspect"> <aop:pointcut id="controllerPointcut" expression="execution(public * com.cnanational.productsservice.controller.*.*(..))"/> <aop:around method="logTimeElapsed" pointcut-ref="controllerPointcut"/> </aop:aspect> </aop:config> </beans>
package com.cnanational.productservice;
import java.util.Properties;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Profile;
import org.springframework.context.support.ResourceBundleMessageSource;
import com.cnanational.productservice.config.ProductProperties;
import com.cnanational.servicecommon.config.CommonConfiguration;
#SpringBootApplication
#EnableConfigurationProperties({ ProductProperties.class })
#Import({ CommonConfiguration.class })
#ImportResource({ "classpath:/product-service/context.xml" })
public class ProductServiceApplication
{
public static final String PROFILE_TEST = "test";
public static void main(String[] args)
{
new SpringApplicationBuilder(ProductServiceApplication.class)
.sources(ProductServiceApplication.class)
.properties(properties())
.run(args);
}
public static Properties properties()
{
Properties props = new Properties();
props.put("spring.config.location", "classpath:product-service/");
props.put("spring.config.name", "product-service");
props.put("logging.config", "classpath:product-service/logback.xml");
return props;
}
#Bean
public MessageSource messageSource()
{
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("product-service/ValidationMessages");
messageSource.setUseCodeAsDefaultMessage(false);
return messageSource;
}
/**
* JUnit test configuration only to be applied when test profile
* is active. This is required for inherited tests to work without
* having to add the EnableAutoConfiguration tag to every concrete
* test class.
*/
#Profile(PROFILE_TEST)
#Configuration
#EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
public static class TestConfiguration
{
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* Let us know that the test configuration is loaded.
*/
#PostConstruct
public void postConstruct()
{
log.info("TestConfiguration loaded");
}
}
}
Anybody know why it doesn't work?
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
06/04/2017 14:11:24.732 ERROR [main] - org.springframework.boot.SpringApplication: Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:742)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151)
at com.cadit.web.WebApplicationAware.main(WebApplicationAware.java:19)
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
at org.springframework.util.Assert.notEmpty(Assert.java:277)
at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.<init>(JpaMetamodelMappingContext.java:52)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:71)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:26)
at org.springframework.beans.factory.config.AbstractFactoryBean.afterPropertiesSet(AbstractFactoryBean.java:134)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 16 common frames omitted
I defined entities in com.cadit.entities:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="TEST")
public class GenericBeans implements BeanType, IEntity<Long> {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "TEST_PAID")
protected Long id;
#Column(name = "SOCIETA")
private String SocietaCod;
#Column(name = "CONTO_INTERMEDIARIO")
private String contoInt;
#Column(name = "TIPO_OPERAZIONE")
private String tipoOpe;
public GenericBeans(String societaCod, String contoInt, String tipoOpe) {
SocietaCod = societaCod;
this.contoInt = contoInt;
this.tipoOpe = tipoOpe;
}
public GenericBeans() {
}
public String getSocietaCod() {
return SocietaCod;
}
public void setSocietaCod(String societaCod) {
SocietaCod = societaCod;
}
public String getContoInt() {
return contoInt;
}
public void setContoInt(String contoInt) {
this.contoInt = contoInt;
}
public String getTipoOpe() {
return tipoOpe;
}
public void setTipoOpe(String tipoOpe) {
this.tipoOpe = tipoOpe;
}
#Override
public String toString() {
return "CSV [SocietaCod=" + SocietaCod + ", contoInt=" + contoInt + ", tipoOpe=" + tipoOpe + "]";
}
#Override
public Long getId() {
return this.id;
}
#Override
public void setId(Long id) {
this.id=id;
}
}
I definied my datasource entry definition for spring:
import org.apache.log4j.Logger;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#ComponentScan
#EntityScan("com.cadit.entities")
//#EnableJpaRepositories("com.cadit.entities")
#EnableTransactionManagement
#PropertySource("classpath:db-config.properties")
public class DbAutoConfiguration {
static final Logger logger = Logger.getLogger(DbAutoConfiguration.class);
public DbAutoConfiguration() {
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
//DataSource ds =new EmbeddedDatabaseBuilder().addScript("classpath:sql/schema.sql").addScript("classpath:testdb/data.sql").build();
DataSourceBuilder ds = DataSourceBuilder.create();
logger.info("dataSource = " + ds);
return ds.build();
}
}
My db-config.properties is:
spring.jpa.hibernate.ddl-auto: validate
spring.jpa.hibernate.naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
#spring.jpa.database: SQL
spring.jpa.show-sql: true
spring.datasource.driverClassName=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://localhost:1433;databaseName=example
spring.datasource.username=xxx
spring.datasource.password=xxx
IEntity is:
public interface IEntity <I extends Serializable> extends Serializable{
/**
* Property rappresenta la primary key.
*/
String P_ID = "id";
/**
* restituisce la primary key
* #return
*/
I getId();
/**
* imposta la primary key
* #param id
*/
void setId(I id);
}
I try to write CSV file to database using CrudRepository interface of spring:
import java.io.File;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.repository.CrudRepository;
import com.cadit.entities.GenericBeans;
import com.csvreader.CsvReader;
public class CsvReaders {
static final Logger logger = Logger.getLogger(CsvReader.class);
#Autowired
public CrudRepository<GenericBeans,Long> _entitymanager;
public List loadDataFromCsv(String fileName) {
try {
File file = new ClassPathResource(fileName).getFile();
CsvReader csv = new CsvReader(file.getAbsoluteFile().getPath(),';');
csv.readHeaders();
List l = new LinkedList();
GenericBeans b = new GenericBeans ();
while (csv.readRecord())
{
b.setSocietaCod(csv.get(0));
b.setContoInt(csv.get(1));
b.setTipoOpe(csv.get(2));
_entitymanager.save(b); //persist on db
l.add(b);
b = new GenericBeans();
}
b=null;
return l;
} catch (Exception e) {
logger.error("Error occurred while loading object list from file " + fileName, e);
return Collections.emptyList();
}
}
}
I DO NOT use main class but a class which extend SpringBootServletInitializer because i want to run it on both standalone tomcat and Tomcat installation as WAR application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages={"com.cadit.entities","com.cadit.beans"})
#EnableAutoConfiguration
public class WebApplicationAware extends SpringBootServletInitializer {
private static Class<WebApplicationAware> applicationClass = WebApplicationAware.class;
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
}
All properties file are in classpath resources because it's a maven project.
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>xxxx</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.1.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<!-- altre dipendenze non spring -->
<!-- https://mvnrepository.com/artifact/net.sourceforge.javacsv/javacsv -->
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
<version>2.0</version>
</dependency>
<!-- per jpa solo se si usa il Tomcat embedded -->
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- end -->
<!-- dipendenze logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<!-- fine dip logback -->
</dependencies>
<properties>
<start-class>hello.WebApplicationAware</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
What's the problem, why doesn't it find JPA entities when I run WebApplicationAware class?
Spring does not find any JPA Entities, so no JPA Meta Model is created, that is why you face the exception.
The cause of this problem may be a wrong persistence-api version on your class path.
You are using
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
but I am pretty shure your spring version uses persistence-api version 2.
Could it be, you are using #Entity annotation from version 1 ?
At runtime spring uses version 2, and this is searching for Entites using #Entity from version 2 only !
Remove the dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.1.RELEASE</version>
</dependency>
Instead add
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
This will give you all JPA dependencies in the right version.
I solved it by adding 2 annotations
#EnableAutoConfiguration
#EntityScan(basePackages = { "com.wt.rds" })
and my dependency was in gradle
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.4.RELEASE'
Unfortunately, most of the springboot guides on JPA integration test often lack a piece of configuration here and there.
So here is an example that hopefully should just work for you.
Point 1.
My local environment is currently setup to use springboot version:
<version.spring.boot>1.5.9.RELEASE</version.spring.boot>
That being said, I am currently setting up my local environment to be able to run integration tests against multiple databases (e.g. postgres, hsql, h2).
Therefore, I start by googling any random toturial that approaches this problem.
The next link is one such example:
https://www.baeldung.com/spring-testing-separate-data-source
The above example is a good starting point. It allows you to scoop up a valid Entity and a Valid repository. The springboot test class itself, on the other hand, leaves a lot ot be desired.
With the above example, you will immediately struggle with the integration test. You will get the usuable problems about the example not giving you the application.class to configure the integration test, and you are left hanging clueless as to what springboot annotations you need to put "where" to make the test to finally run without explosions.
So now I give you a MINIMAL set of 3 classes (Entity + Repository + SpringbootTest) that should hopefully have 100 percent of the configuration you need. This will serve as a basis of any JPA based integration test you will need to do in the future, then you can swap your entities and repositories, and continue testing with the same type of srpingboot configuration.
I start by giving you the IRRELEVANT classes. The stuff that is always the same, the stuff that you want to test, and that has nothing to do with configuration.
I am referring to REPOSITORY + ENTITY.
In eclipse create your java package:
tutorial.www.baeldung.com.tutorial001jpa.separateDS
Dump into this package the following trivial entity and repository classes, that are based on the tutorial reference I gave above.
Tutorial001GenericEntity
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "TUTORIAL_001_GENERIC_ENTITY")
public class Tutorial001GenericEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String value;
public Tutorial001GenericEntity() {
super();
}
public Tutorial001GenericEntity(String value) {
super();
this.value = value;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
// standard constructors, getters, setters
}
Then we go for the second trivial code snippet.
The spring repository boiler plate code.
Tutorial001GenericEntityRepository
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import org.springframework.data.jpa.repository.JpaRepository;
public interface Tutorial001GenericEntityRepository extends JpaRepository<Tutorial001GenericEntity, Long> {
}
At this point your maven project, src/test/java has a total of two classes. The basic stuff.
An entity and a repository, that serve as an example of any integration test you will ever need to do.
So now you go to the only important class in the example, the stuff that always gives a lot of problems, and that is the springboot test class which more then being responsible to test your business logic also has the complex task of CONFIGURING your test.
In this case, this test class has ALL IN ONE the annotations that allow springboot to disocver your entities, repositories, etc...
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
#SpringBootTest()
public class Tutorial001GenericEntityIntegrationTest {
#EntityScan(basePackageClasses = { Tutorial001GenericEntity.class })
#EnableJpaRepositories(basePackageClasses = Tutorial001GenericEntity.class)
#EnableAutoConfiguration()
public static class ConfigureJpa {
}
#Autowired
private Tutorial001GenericEntityRepository genericEntityRepository;
#Test
public void givenTutorial001GenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() {
Tutorial001GenericEntity genericEntity = genericEntityRepository.save(new Tutorial001GenericEntity("test"));
Tutorial001GenericEntity foundEntity = genericEntityRepository.findOne(genericEntity.getId());
assertNotNull(foundEntity);
assertEquals(genericEntity.getValue(), foundEntity.getValue());
}
}
The important thing, you see, is that this spring boot test has a class level annotation to provide to the springboot test the configuration context.
What we are doing is dumping one and only one class reference that represents our test configuration.
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class
And then on this little guy, you put all of the additional annotations in the world you need that springboot offers to configure applications.
In this case we have a dedicated annotation to mention entities.
Another to mention repositories.
And another to tell springboot to activate its auto configuration.
This springboot auto configuration annotation then does additional vodoo, like looking at your classpath and seeing that you have in the classpath say:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
<version>2.3.4</version>
</dependency>
And it will immediately know how to configure an in memory data source for this database.
Behind the scenes, there might be additional configuration that is getting used.
For example, if you create an application.properties file in your src/test/resources that file will be considered.
It is very to see that the appliction.properties is considered by your running test.
If you want to verify this, make sure that in your test setup you do not have, for example, any dependency on the JDBC driver for postgres.
And then put into your application.properties something liek this:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
This dialect is not compatible with HSQL or H2, so it will immediately make your green passing integration test blow up.
To be honest, I do not know if there is a simpler combo of annotations to properly configure the springboot scanning for an integration test.
As a rule, I would recommend that you try avoiding having hundreds of thousands of configuration classes in your src/test/resources.
Because if at some point you want to toggle all of your integration tests from using applicat-postgres.proeprties to application-hsql.properties, you might find yourself needing to tweak multiple configuration classes instead of just one.
So as rule, per maven component you write, I would try to have the tests that check repositories extend some sort of MyBaseINtegrationTestClass, and in there put this
#ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
So that you only need to play with one configuration for testing for the hole project.
IN any case, hopefully the triplet of classes given here helps you.
One finel thing, for maven dependencies for integration testing, here is what I am using:
<!-- Test Dependencies JPA REPOSITORY TESTS -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
The reason why i am using hsql and h2 is beacuse I want my integration tests to be able to be tunned to either use application-hsql or application-h2.properties.