I am trying to configure a Postgres database in spring boot using annotation configuration. I have all the database credentials in a file named database.properties and the configuration file is called DBconfig.java
database.url= jdbc:postgresql://localhost/mydb
database.driverClassName= com.postgresql.jdbc.Driver
database.username= postgres
database.password= password
The dbConfig file -
#Configuration
#PropertySource("classpath:databaseAccess/database.properties")
public class DBconfig {
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Value("${url}")
private String url;
#Bean
#Qualifier("postgresDB")
public DataSource dataSource() {
DataSourceBuilder dataSource = DataSourceBuilder.create();
dataSource.url(url);
dataSource.password(password);
//dataSource.driverClassName(driverClassName);
dataSource.username(username);
return dataSource.build();
}
#Bean
#Qualifier("jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
This is my main file
#SpringBootApplication
public class GetNotificationsApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(DBconfig.class);
JdbcTemplate template= ctx.getBean("jdbcTemplate", JdbcTemplate.class);
template.execute("CREATE TABLE TEST( test VARCHAR(20))");
}
}
I keep getting the error
Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalArgumentException: URL must start with 'jdbc'
Try to change value for url parameter by defining port number for postgres. Assuming that postgres is running on 5432 which is the default port.
Change
jdbc:postgresql://localhost/mydb
To
jdbc:postgresql://localhost:5432/mydb
For port number check Find the host name and port using PSQL commands
UPDATE:
Also change #Value("${username}") to #Value("${database.username}") and other properties too by prefix database.
Related
I'm trying to use a docker container to run the tests of a springboot microservices application. The problem is that the application makes the connection to my machine instead of the docker container that holds the database.
This is my first time doing this so I'm guessing this is a configuration problem but, every time I look for instructions, it seems that the annotations #Testcontainers and #Container are enough to make sure springboot uses the container.
Here's my code:
#SpringBootTest
#Testcontainers
#AutoConfigureMockMvc
class ProductServiceApplicationTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private ProductRepository productRepository;
#Container
static PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:12.13")
.withDatabaseName("postgreSQLContainer")
.withUsername("test")
.withPassword("test");
static void setProperties(DynamicPropertyRegistry dynamicPropertyRegistry) {
dynamicPropertyRegistry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
dynamicPropertyRegistry.add("spring.datasource.username", postgreSQLContainer::getUsername);
dynamicPropertyRegistry.add("spring.datasource.password", postgreSQLContainer::getPassword);
}
#Test
void shouldCreateProduct() throws Exception {
ProductRequest productRequest = getProductRequest();
String productRequestString = objectMapper.writeValueAsString(productRequest);
mockMvc.perform(MockMvcRequestBuilders.post("/api/product")
.contentType(MediaType.APPLICATION_JSON)
.content(productRequestString)
).andExpect(status().isCreated()); //THIS IS OK
Assertions.assertTrue(productRepository.findAll().size() == 1); //THIS FAILS
}
private ProductRequest getProductRequest() {
return ProductRequest.builder()
.name("some-product")
.description("some-description")
.price(BigDecimal.valueOf(10))
.build();
}
}
Debugging this issue I saw that the repository was hitting the postgresql database that I had local.
And if I shotdown my local postgresql service this is what happens:
org.postgresql.util.PSQLException: Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
You are missing #DynamicPropertySource annotation on your setProperties method. Spring doesn't know that you are providing properties without this annotation.
I'm developing an easy sample application. One component is a gateway service using Spring Boot and Reactive Spring Data for Mongo, because that's where I want to store user and login informations.
For testing out different solutions, I wanted to use MongoDB Atlas. So, I set up an application. But when I want to save just a sample user, nothing happens, the data is not saved to the database. However it looks like the application is connected to the MongoDb Atlas. No error logs about failed connections.
This is the main class, where I have the #EnableReactiveMongoRepositories annotation:
#SpringBootApplication
#EnableReactiveMongoRepositories("com.bkk.sm.authentication.repository")
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
Here is how I set up Mongo in application.yml and the repository:
spring:
data:
mongodb:
database: users
uri: mongodb+srv://${MONGO_USER}:${MONGO_PASSWORD}#taocluster.qa3sd.mongodb.net/users?retryWrites=true&w=majority
#Repository
public interface ReactiveUserRepository extends ReactiveMongoRepository<User, String> {
Mono<User> findByUsername(String username);
}
I don't use any specific reactive MongoDB config, I don't extend the AbstractReactiveMongoConfiguration (this is really just a bout to experiment how does this work) and I use the defaults.
In my UserDetailsServiceImpl, I try to save a sample record, just right after the bean is constructed:
#Slf4j
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private ReactiveUserRepository repository;
public UserDetailsServiceImpl(ReactiveUserRepository repository) {
this.repository = repository;
}
#PostConstruct
public void setup() {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String pwd = encoder.encode("user");
User user = User.builder()
.username("user")
.password(pwd)
.accountExpired(false)
.accountLocked(false)
.activationCode(null)
.activatedTime(Date.from(Instant.now()))
.email("user#user.com")
.enabled(true)
.firstName("User")
.failedLoginAttempts(0)
.lastModificationTime(Date.from(Instant.now()))
.lastName("User")
.middleName("User")
.passwordExpiryTime(Date.from(Instant.now()))
.registrationTime(Date.from(Instant.now()))
.roles(List.of(CompanyRole.builder().companyCode("bkk")
.companyName("Beszterce KK")
.role(Role.ROLE_USER)
.build())
)
.passwordExpiryTime(null)
.version(0)
.build();
this.repository.save(user).map(user1 -> {
log.info("User saved. {}", user1);
return user1;
}).onErrorResume(Objects::nonNull, throwable -> {
log.error("Something is not right here.", throwable);
return Mono.error(throwable);
}).switchIfEmpty(Mono.defer(() -> {
log.info("Cannot save ure={}", user.toString());
return Mono.error(new Exception("WTF?"));
}));
}
... SOME MORE METHODS COME HERE
}
When it executes the this.repository.save(user) line, nothing happens. Well, I tried to debug and went deeper into the framework but ultimately, nothing happens. That's why I added some log messages. But nothing. If I put a breakpoint to the map or onErrorResume or switchIfEmpty branches, the execution doesn't stop there. No log is written to console other that this line:
2022-04-09 00:02:46.061 INFO 72528 --- [ntLoopGroup-3-7] org.mongodb.driver.connection : Opened connection [connectionId{localValue:7, serverValue:78530}] to taocluster-shard-00-02.qa3sd.mongodb.net:27017
And here is my data object where I declare the collection name:
#Getter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
#Indexed
#NonNull
private String username;
... SOME MORE FIELDS COME HERE ...
}
So, my question is, what am I doing wrong? Why I don't see anything added to my MongoDB Atlas sample database? Where I just set the 0.0.0.0/0 for accepting connections from everywhere for the time being of testing this stuff out.
Any help would be appreciated.
I’m trying to update the Camunda DMN table programmatically and deploy it again after the update.
But while creating a process engine, getting the exception for H2 driver, but for my given project I’m using the PostgreSQL database for Camunda tables.
ProcessEngine processEngine = ProcessEngineConfiguration
.createStandaloneInMemProcessEngineConfiguration().buildProcessEngine();
org.camunda.bpm.engine.repository.Deployment deployment = processEngine.getRepositoryService()
.createDeployment()
.addString(fileName, Dmn.convertToString(dmnModelInstance))
.name("Deployment after update").deploy();
java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: org.h2.Driver
at org.apache.ibatis.datasource.unpooled.UnpooledDataSource.initializeDriver(UnpooledDataSource.java:221)
at org.apache.ibatis.datasource.unpooled.UnpooledDataSource.doGetConnection(UnpooledDataSource.java:200)
at org.apache.ibatis.datasource.unpooled.UnpooledDataSource.doGetConnection(UnpooledDataSource.java:196)
at org.apache.ibatis.datasource.unpooled.UnpooledDataSource.getConnection(UnpooledDataSource.java:93)
at org.apache.ibatis.datasource.pooled.PooledDataSource.popConnection(PooledDataSource.java:385)
at org.apache.ibatis.datasource.pooled.PooledDataSource.getConnection(PooledDataSource.java:89)
at org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl.initDatabaseType(ProcessEngineConfigurationImpl.java:1300)
You need to create datasource bean explicitly or can declare the datasource attributes in bootstrap.yml or application.properties file.
#Configuration
public class ExampleProcessEngineConfiguration {
#Bean
public DataSource dataSource() {
// Use a JNDI data source or read the properties from
// env or a properties file.
// Note: The following shows only a simple data source
// for In-Memory H2 database.
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(org.h2.Driver.class);
dataSource.setUrl("jdbc:h2:mem:camunda;DB_CLOSE_DELAY=-1");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean
public SpringProcessEngineConfiguration processEngineConfiguration() {
SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setDataSource(dataSource());
config.setTransactionManager(transactionManager());
config.setDatabaseSchemaUpdate("true");
config.setHistory("audit");
config.setJobExecutorActivate(true);
return config;
}
#Bean
public ProcessEngineFactoryBean processEngine() {
ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean();
factoryBean.setProcessEngineConfiguration(processEngineConfiguration());
return factoryBean;
}
#Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
#Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
#Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
// more engine services and additional beans ...
}
When Mongodb is down, the spring boot application is down. I wish to handle exception of connectiontimeout and log the error without stopping application.
When finding an item from database is failed because the connection is not possible, the application should do another treatment like calling web service to find data.
Did you have any idea about this ?
Configuration
spring.data.mongodb.uri=mongodb://${MONGODB_DB_HOST}:${MONGODB_DB_PORT}/${MONGODB_DB_DATABASE}?connectTimeoutMS=${mongodb.connection.timeout}
I have used below code to configure mongodb connection in spring boot
you can specify socket timeout and connection timeout according to your need.
#Configuration
public class DatabaseConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseConfiguration.class);
#Value("${spring.data.mongodb.uri}")
private String mongoUri;
#Value("${spring.data.mongodb.database}")
private String mongoDbName;
#Value("${spring.data.mongodb.host}")
private String host;
#Value("${spring.data.mongodb.port}")
private int port;
#Value("${spring.data.mongodb.username}")
private String username;
#Value("${spring.data.mongodb.password}")
private String password;
#Bean
public MongoTemplate mongoTemplate() {
LOGGER.debug(" instantiating MongoDbFactory ");
SimpleMongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongoClient(), mongoDbName);
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
return mongoTemplate;
}
#Bean
public MongoClient mongoClient() {
List<ServerAddress> servers = new ArrayList<ServerAddress>();
servers.add(new ServerAddress(host, port));
MongoClientOptions mongoClientOptions = MongoClientOptions.builder()
.connectionsPerHost(10)
.socketTimeout(2000)
.connectTimeout(2000)
.build();
if (Utils.isNotEmpty(username) && Utils.isNotEmpty(password)) {
List<MongoCredential> creds = new ArrayList<MongoCredential>();
creds.add(MongoCredential.createCredential(username, mongoDbName, password.toCharArray()));
return new MongoClient(servers,creds, mongoClientOptions);
} else
return new MongoClient(servers, mongoClientOptions);
}
#Bean
public MongoClientURI mongoClientURI() {
LOGGER.debug(" creating connection with mongodb with uri [{}] ", mongoUri);
return new MongoClientURI(mongoUri);
}
}
Define below properties in your application.yml file
mongodb specific properties
spring:
data:
mongodb:
database: dbname
host: localhost
port: 27017
username: dbusername
password: dbpassword
You can use the below sample example to configure mongodb timeout. I hope it's easy to convert to bean annonation for springboot. Else you can import the resource bean (#ImportResource)
<beans>
<mongo:mongo host="localhost" port="27017">
<mongo:options connections-per-host="8"
threads-allowed-to-block-for-connection-multiplier="4"
connect-timeout="1000"
max-wait-time="1500}"
auto-connect-retry="true"
socket-keep-alive="true"
socket-timeout="1500"
slave-ok="true"
write-number="1"
write-timeout="0"
write-fsync="true"/>
</mongo:mongo/>
</beans>
I am trying use MongoDB, Morphia and Spring and test it, so I started use Embedded Mongo.
When I had only one DAO to persist I did not had any problem with my tests, however, in some cases I needed use more than one DAO, and in that cases my injected Datasore give me an problem: addr already in use.
My Spring Test Database Configuration is this:
#Configuration
public class DatabaseMockConfig {
private static final int PORT = 12345;
private MongodConfigBuilder configBuilder;
private MongodExecutable mongodExecutable;
private MongodProcess mongodProcess;
#Bean
#Scope("prototype")
public MongodExecutable getMongodExecutable() {
return this.mongodExecutable;
}
#Bean
#Scope("prototype")
public MongodProcess mongodProcess() {
return this.mongodProcess;
}
#Bean
public IMongodConfig getMongodConfig() throws UnknownHostException, IOException {
if (this.configBuilder == null) {
configBuilder = new MongodConfigBuilder().version(Version.Main.PRODUCTION).net(new Net(PORT, Network.localhostIsIPv6()));
}
return this.configBuilder.build();
}
#Autowired
#Bean
#Scope("prototype")
public Datastore datastore(IMongodConfig mongodConfig) throws IOException {
MongodStarter starter = MongodStarter.getDefaultInstance();
this.mongodExecutable = starter.prepare(mongodConfig);
this.mongodProcess = mongodExecutable.start();
MongoClient mongoClient = new MongoClient("localhost", PORT);
return new Morphia().createDatastore(mongoClient, "morphia");
}
#Autowired
#Bean
#Scope("prototype")
public EventDAO eventDAO(final Datastore datastore) {
return new EventDAO(datastore);
}
#Autowired
#Bean
#Scope("prototype")
public EditionDAO editionDAO(final Datastore datastore) {
return new EditionDAO(datastore);
}
}
And my DAO classes are similar to that
#Repository
public class EventDAO {
private final BasicDAO<Event, ObjectId> basicDAO;
#Autowired
public EventDAO(final Datastore datastore) {
this.basicDAO = new BasicDAO<>(Event.class, datastore);
}
...
}
My test class is similar to that:
#ContextConfiguration(classes = AppMockConfig.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class EventDAOTest {
#Autowired
private EventDAO eventDAO;
#Autowired
private MongodExecutable mongodExecutable;
#Autowired
private MongodProcess mongodProcess;
#Rule
public ExpectedException expectedEx = ExpectedException.none();
#After
public void tearDown() {
this.mongodProcess.stop();
this.mongodExecutable.stop();
}
...
}
I use prototype scope to solve problem with singleton and make sure that my mock database is clean when I start my test, after that I stop mongod process and mongod executable.
However since I need use more than one DAO I receive that error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'editionDAO' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.mongodb.morphia.Datastore]: :
Error creating bean with name 'datastore' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.mongodb.morphia.Datastore]:
Factory method 'datastore' threw exception; nested exception is java.io.IOException: Could not start process: ERROR: listen(): bind() failed errno:98 Address already in use for socket: 0.0.0.0:12345
2015-01-04T01:05:04.128-0200 [initandlisten] ERROR: addr already in use
I know what the error means, I just do not know how can I design my Configuration to solve that. As last option I am considering install a localhost MongoDB just for tests, however I think could be a better solution
That is based on the embedded mongod by flapdoodle, right?
If you want to run multiple tests in parallel (could be changed via JUnit annotations, but it's probably faster in parallel), you cannot use a single, hardcoded port. Instead, let the embedded process select an available port automatically.