Spring Configuration Properties Nested Custom Validation - spring

I have nested properties class:
#ConfigurationProperties(prefix = "myapp", ignoreUnknownFields = false)
public class MyAppProperties implements Validator {
#Valid
private List<Server> servers = new ArrayList();
public MyAppProperties() {
}
public List<Server> getServers() {
return this.servers;
}
public void setServers(List<Server> servers) {
this.servers = servers;
}
#Override
public boolean supports(Class<?> clazz) {
return MyAppProperties.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
MyAppProperties properties = (MyAppProperties) target;
if (isEmpty(properties.getServers())) {
errors.rejectValue("servers", "myapp.servers", "Servers not provided");
}
}
public static class Server implements Validator {
private String name;
private String url;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
#Override
public boolean supports(Class<?> clazz) {
return Server.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Server server = (Server) target;
if (StringUtils.isBlank(server.getName())) {
errors.rejectValue("name", "server.name", "Server name not provided");
}
if (StringUtils.isBlank(server.getUrl())) {
errors.rejectValue("url", "server.url", "Server url not provided");
}
}
}
}
Now i want the validation to fire for both classes, but when i run with invalid values, the validation fires only for MyAppProperties but not for the list of Servers.
Am i doing something wrong, i'm not able to make this work. I want to use custom validator for both. Can anybody please help me make this work.
The invalid values that i pass are:
myapp.servers[0].name=
myapp.servers[0].url=
But this runs without any error

Related

MessageMapping in WebSockets Spring is not being called even though stomp sent is working

Message Mapping Controller
#Controller
public class MessageControl {
#MessageMapping("/message")
#SendTo("/topic/return")
public Message getContent(Message message) {
//to call this method we call /app/message
// try {
// //processing
//// Thread.sleep(2000);
//
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("Returning message to subscribing items");
return message;
}
}
Send function in JavaScript from client side
function sendMessage(){
let jsonOb={
name:localStorage.getItem("name"),
content:$("#message-value").val()
}
stompClient.send("/app/message",{},JSON.stringify(jsonOb));
}
Configuration Class
#Configuration
#EnableWebSocketMessageBroker
public class Config implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/server1").withSockJS();
//this is the link where the client connects to the backend server
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic","/queue");
registry.setApplicationDestinationPrefixes("/app");
//any request that is being sent to the broker needs to be through /app/topic
}
}
Message class
public class Message {
private String name;
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public Message(String name, String content) {
super();
this.name = name;
this.content = content;
}
public void setContent(String content) {
this.content = content;
}
}
The browser console shows that the message is being sent to the server, but the server side Method defined for the message mapping does not get called, the system.out.println("") print statement does not get printed. So it means the mapping is not working for some reason.

Custom-user-federation does not add to keycloak admin panel

I have implemented my user-storage-provider like this:
public class UserStorageProvider implements org.keycloak.storage.UserStorageProvider, UserLookupProvider, CredentialInputValidator {
public UserStorageProvider(KeycloakSession session) {
this.session = session;
}
public UserStorageProvider(KeycloakSession session, ComponentModel model) {
this.session = session;
this.model = model;
}
protected KeycloakSession session;
protected ComponentModel model;
#Override
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
return true;
}
#Override
public boolean supportsCredentialType(String credentialType) {
return true;
}
#Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
return true;
}
#Override
public UserModel getUserByUsername(String username, RealmModel realm) {
//here to fetch user from my DB.
return null;
}
#Override
public UserModel getUserById(String id, RealmModel realm) {
StorageId storageId = new StorageId(id);
String username = storageId.getExternalId();
return getUserByUsername(username, realm);
}
#Override
public UserModel getUserByEmail(String email, RealmModel realm) {
return null;
}
public void close() {
}
}
and its factory class:
public class UserStorageProviderFactory implements org.keycloak.storage.UserStorageProviderFactory<UserStorageProvider> {
public static final String PROVIDER_NAME = "user-provider";
#Override
public String getHelpText() {
return "JPA Example User Storage Provider";
}
#Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
#Override
public UserStorageProvider create(KeycloakSession keycloakSession) {
return new UserStorageProvider(keycloakSession);
}
#Override
public UserStorageProvider create(KeycloakSession keycloakSession, ComponentModel componentModel) {
UserStorageProvider provider = new UserStorageProvider(keycloakSession, componentModel);
provider.session = keycloakSession;
provider.model = componentModel;
return provider;
}
#Override
public String getId() {
return PROVIDER_NAME;
}
#Override
public void init(Config.Scope config) {
}
#Override
public void postInit(KeycloakSessionFactory factory) {
}
#Override
public void close() {
}
}
and the org.keycloak.storage.UserStorageProviderFactory file is located on META-INF/services/ and the its content is:
com.kian.neshan.userfederation.UserStorageProviderFactory
So I make the jar by mvn clean package and put it in deplyment folder of keycloak but when I go to admin panel, my provider is not added to user-federation option
Where is wrong?
The whole of my module is correct.
The problem is the local-keycloak-docker-image shuold be removed after any changes of the module
Keycloak is started by docker-compose and its images is built by Dockerfile, so after any changes of my module, the local-keycloak-image should be removed by sudo docker rmi [local-image-name] to rebuild the image to get the newest my jar file and put it to deployment folder of local-docker-image.

EmptyResultDataAccessException when testing Spring controller

In my app, there is a controller, a service, a repo and a class. I am writing unit test to verify my PUT request. In postman, the put request works fine, however, when testing in JUnit test, it throws EmptyResultDataAccessException eror. Many other tests have the same problem and all of them require to find a specific entry in the repo by id. I think this is the problem. Please help me on this.
#Data
#Entity
public class ErrorMessage {
private #Id #GeneratedValue Long id;
private String message;
private int code;
public ErrorMessage() {
}
public ErrorMessage(int code, String message) {
this.code = code;
this.message = message;
}
}
#Repository
interface ErrorMessageRepository extends JpaRepository<ErrorMessage, Long> {
List<ErrorMessage> findByCode(int code);
}
#Service
public class ErrorMessageService {
#Autowired
ErrorMessageRepository repository;
#Transactional
public List<ErrorMessage> getAll()
{
return repository.findAll();
}
#Transactional(readOnly = true)
public Optional<ErrorMessage> getById(Long id)
{
return repository.findById(id);
}
#Transactional(readOnly = true)
public List<ErrorMessage> getByCode(int code)
{
return repository.findByCode(code);
}
#Transactional
public ErrorMessage saveOne(ErrorMessage messages)
{
return repository.save(messages);
}
#Transactional
public Optional<ErrorMessage> deleteById(long id)
{
Optional<ErrorMessage> em = repository.findById(id);
repository.deleteById(id);
return em;
}
#Transactional
public ErrorMessage updateById(long id, ErrorMessage newMessage)
{
ErrorMessage m = repository.findById(id).get();
m.setCode(newMessage.getCode());
m.setMessage(newMessage.getMessage());
repository.save(m);
return m;
}
}
class ErrorMessageController {
private static final Logger log = LoggerFactory.getLogger(ErrorMessageController.class);
#Autowired
ErrorMessageRepository repository;
#Autowired
private ErrorMessageService ems;
#GetMapping("/errormessages")
public List<ErrorMessage> getAll() {
return ems.getAll();
}
#GetMapping("/errormessagesbycode/{code}")
public List<ErrorMessage> getByCode(#PathVariable int code) {
return ems.getByCode(code);
}
#GetMapping("/errormessage/{id}")
ErrorMessage getById(#PathVariable Long id) {
return ems.getById(id)
.orElseThrow(() -> new MessageNotFoundException(id));
}
#PostMapping("/errormessage")
ErrorMessage newMessage(#RequestBody ErrorMessage newMessage) {
return ems.saveOne(newMessage);
}
#DeleteMapping("/errormessage/{id}")
Optional<ErrorMessage> deleteMessage(#PathVariable Long id) {
return ems.deleteById(id);
}
#PutMapping("/errormessage/{id}")
ErrorMessage updateMessage(#PathVariable Long id, #RequestBody ErrorMessage newMessage) {
return ems.updateById(id, newMessage);
}
}
#SpringBootTest
#AutoConfigureMockMvc
public class ErrorMessageTest {
private static ErrorMessage em, emId;
private static ObjectMapper mapper;
#Autowired
private MockMvc mockMvc;
#BeforeAll
public static void init() throws Exception {
mapper = new ObjectMapper();
em = new ErrorMessage(400, "bad request0");
emId = new ErrorMessage(400, "bad request0");
emId.setId(Long.valueOf(1));
}
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
}
Try this
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
ErrorMessageService errorMessageService = Mockito.mock(ErrorMessageService.class);
Mockito.when(errorMessageService.updateById(Mockito.any(), Mockito.any())).thenReturn(modifiedMessage);
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
I found out the bug. The order of the unit test is random. All i need to do is use #Order to ensure the order.

Spring Boot - Test - Validator: Invalid target for Validator

I'm getting the following error when I'm trying to run a test:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Invalid target for Validator [userCreateFormValidator bean]: com.ar.empresa.forms.UserCreateForm#15c3585
Caused by: java.lang.IllegalStateException: Invalid target for Validator [userCreateFormValidator bean]: com.ar.empresa.forms.UserCreateForm#15c3585
at org.springframework.validation.DataBinder.assertValidators(DataBinder.java:567)
at org.springframework.validation.DataBinder.addValidators(DataBinder.java:578)
at com.ar.empresa.controllers.UserController.initBinder(UserController.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
The code is:
Controller:
#Controller
public class UserController {
private UserService userService;
private UserCreateFormValidator userCreateFormValidator;
#Autowired
public UserController(UserService userService, UserCreateFormValidator userCreateFormValidator) {
this.userService = userService;
this.userCreateFormValidator = userCreateFormValidator;
}
#InitBinder("form")
public void initBinder(WebDataBinder binder) {
binder.addValidators(userCreateFormValidator);
}
#PreAuthorize("hasAuthority('ADMIN')")
#RequestMapping(value = "/user/create", method = RequestMethod.GET)
public ModelAndView getUserCreatePage() {
return new ModelAndView("user_create", "form", new UserCreateForm());
}
#PreAuthorize("hasAuthority('ADMIN')")
#RequestMapping(value = "/user/create", method = RequestMethod.POST)
public String handleUserCreateForm(#Valid #ModelAttribute("form") UserCreateForm form, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "user_create";
}
try {
userService.create(form);
} catch (DataIntegrityViolationException e) {
bindingResult.reject("email.exists", "Email already exists");
return "user_create";
}
return "redirect:/users";
}
}
Validator:
#Component
public class UserCreateFormValidator implements Validator {
private final UserService userService;
#Autowired
public UserCreateFormValidator(UserService userService) {
this.userService = userService;
}
#Override
public boolean supports(Class<?> clazz) {
return clazz.equals(UserCreateForm.class);
}
#Override
public void validate(Object target, Errors errors) {
UserCreateForm form = (UserCreateForm) target;
validatePasswords(errors, form);
validateEmail(errors, form);
}
private void validatePasswords(Errors errors, UserCreateForm form) {
if (!form.getPassword().equals(form.getPasswordRepeated())) {
errors.reject("password.no_match", "Passwords do not match");
}
}
private void validateEmail(Errors errors, UserCreateForm form) {
if (userService.getUserByEmail(form.getEmail()).isPresent()) {
errors.reject("email.exists", "User with this email already exists");
}
}
}
UserCreateForm:
public class UserCreateForm {
#NotEmpty
private String email = "";
#NotEmpty
private String password = "";
#NotEmpty
private String passwordRepeated = "";
#NotNull
private Role role = Role.USER;
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public String getPasswordRepeated() {
return passwordRepeated;
}
public Role getRole() {
return role;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
public void setPasswordRepeated(String passwordRepeated) {
this.passwordRepeated = passwordRepeated;
}
public void setRole(Role role) {
this.role = role;
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserControllerTest {
private MockMvc mockMvc;
private MediaType contentType = new MediaType(APPLICATION_JSON.getType(),
APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#MockBean
private UserService userService;
#MockBean
private UserCreateFormValidator userCreateFormValidator;
#Autowired
FilterChainProxy springSecurityFilterChain;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new UserController(userService,userCreateFormValidator)).apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain)).build();
}
#Test
#WithMockUser(username="user",
password="password",
roles="ADMIN")
public void homePage_authenticatedUser() throws Exception {
mockMvc.perform(get("/user/create"))
.andExpect(status().isOk())
.andExpect(view().name("user_create"));
}
}
I don't know why, because it is a GET method, so it don't have to validate it.
Thanks! :)
You got this exception because you didn't mock the behaviour of public boolean supports(Class<?> clazz) method on your userCreateFormValidator #Mockbean.
If you take a look at the code of org.springframework.validation.DataBinder.assertValidators(DataBinder.java) from the log you posted, you can find there how the validators are processed and how java.lang.IllegalStateException is thrown. In Spring 4.3.8, it looks like this
if(validator != null && this.getTarget() != null && !validator.supports(this.getTarget().getClass())) {
throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + this.getTarget());
}
You didn't mock supports method of the validator and returns false by default, causing Spring code above throw the IllegalStateException.
TLDR, just give me solution:
You have to mock supports method on your validator. Add following to #Before or #BeforeClass method.
when(requestValidatorMock.supports(any())).thenReturn(true);
I cant comment on the correct answer but his solution worked:
Here is what I had to do for this exact error.
//Imports
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
#MockBean
ApiValidationRouter apiValidationRouter;
#Before
public void beforeClass() throws Exception {
when(apiValidationRouter.supports(any())).thenReturn(true);
}

What is the best way to use #ConfigurationProperties with Builders?

I have searched and can't find any examples that would show me a better way to do this, but in the Spring/Spring Boot code, there are generic builders but the builder itself seems to apply the properties programmatically. Here is some code trying to configure 2 Oracle Connection Pool Data Sources:
import oracle.ucp.jdbc.PoolDataSourceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.sql.SQLException;
#Configuration
#EnableConfigurationProperties
#ConditionalOnClass(PoolDataSourceFactory.class)
public class PersistenceAutoConfiguration {
#Bean (name = "readWriteDataSource")
public DataSource getReadWriteDataSource() throws SQLException {
OracleUcpDataSourceProperties rwProperties = getReadWriteProperties();
return OracleUcpDataSourceBuilder.create()
.connectionFactoryClassName(rwProperties.getConnectionFactoryClassName())
.url(rwProperties.getUrl())
.user(rwProperties.getUser())
.password(rwProperties.getPassword())
.initialPoolSize(rwProperties.getInitialPoolSize())
.minPoolSize(rwProperties.getMinPoolSize())
.maxPoolSize(rwProperties.getMaxPoolSize())
.connectionWaitTimeout(rwProperties.getConnectionWaitTimeout())
.inactiveConnectionTimeout(rwProperties.getInactiveConnectionTimeout())
.maxIdleTime(rwProperties.getMaxIdleTime())
.build();
}
#Bean (name = "readOnlyDataSource")
public DataSource getReadOnlyDataSource() throws SQLException {
OracleUcpDataSourceProperties roProperties = getReadOnlyProperties();
return OracleUcpDataSourceBuilder.create()
.connectionFactoryClassName(roProperties.getConnectionFactoryClassName())
.url(roProperties.getUrl())
.user(roProperties.getUser())
.password(roProperties.getPassword())
.initialPoolSize(roProperties.getInitialPoolSize())
.minPoolSize(roProperties.getMinPoolSize())
.maxPoolSize(roProperties.getMaxPoolSize())
.connectionWaitTimeout(roProperties.getConnectionWaitTimeout())
.inactiveConnectionTimeout(roProperties.getInactiveConnectionTimeout())
.maxIdleTime(roProperties.getMaxIdleTime())
.build();
}
#ConfigurationProperties(prefix = "datasource.readwrite")
#Bean(name = "readWriteProperties")
protected OracleUcpDataSourceProperties getReadWriteProperties() {
return new OracleUcpDataSourceProperties();
}
#ConfigurationProperties(prefix = "datasource.readonly")
#Bean(name = "readOnlyProperties")
protected OracleUcpDataSourceProperties getReadOnlyProperties() {
return new OracleUcpDataSourceProperties();
}
}
and
public class OracleUcpDataSourceProperties {
private String connectionFactoryClassName;
private String url;
private String user;
private String password;
private int initialPoolSize;
private int minPoolSize;
private int maxPoolSize;
private int connectionWaitTimeout;
private int inactiveConnectionTimeout;
private int maxIdleTime;
private Boolean validateConnectionOnBorrow;
public String getConnectionFactoryClassName() {
return connectionFactoryClassName;
}
public void setConnectionFactoryClassName(String connectionFactoryClassName) {
this.connectionFactoryClassName = connectionFactoryClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getInitialPoolSize() {
return initialPoolSize;
}
public void setInitialPoolSize(int initialPoolSize) {
this.initialPoolSize = initialPoolSize;
}
public int getMinPoolSize() {
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize) {
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getConnectionWaitTimeout() {
return connectionWaitTimeout;
}
public void setConnectionWaitTimeout(int connectionWaitTimeout) {
this.connectionWaitTimeout = connectionWaitTimeout;
}
public int getInactiveConnectionTimeout() {
return inactiveConnectionTimeout;
}
public void setInactiveConnectionTimeout(int inactiveConnectionTimeout) {
this.inactiveConnectionTimeout = inactiveConnectionTimeout;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public Boolean getValidateConnectionOnBorrow() {
return validateConnectionOnBorrow;
}
public void setValidateConnectionOnBorrow(Boolean validateConnectionOnBorrow) {
this.validateConnectionOnBorrow = validateConnectionOnBorrow;
}
}
and
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import java.sql.SQLException;
public class OracleUcpDataSourceBuilder {
private PoolDataSource pds;
/**
* This will grab the pool factory and initialize it.
*/
public OracleUcpDataSourceBuilder() throws SQLException {
pds = PoolDataSourceFactory.getPoolDataSource();
}
public static OracleUcpDataSourceBuilder create() throws SQLException {
return new OracleUcpDataSourceBuilder();
}
public OracleUcpDataSourceBuilder connectionFactoryClassName(String connectionFactoryClassName) throws SQLException {
pds.setConnectionFactoryClassName(connectionFactoryClassName);
return this;
}
public OracleUcpDataSourceBuilder url(String url) throws SQLException {
pds.setURL(url);
return this;
}
public OracleUcpDataSourceBuilder user(String user) throws SQLException {
pds.setUser(user);
return this;
}
public OracleUcpDataSourceBuilder password(String password) throws SQLException {
pds.setPassword(password);
return this;
}
public OracleUcpDataSourceBuilder initialPoolSize(int initialPoolSize) throws SQLException {
pds.setInitialPoolSize(initialPoolSize);
return this;
}
public OracleUcpDataSourceBuilder minPoolSize(int minPoolSize) throws SQLException {
pds.setMinPoolSize(minPoolSize);
return this;
}
public OracleUcpDataSourceBuilder maxPoolSize(int maxPoolSize) throws SQLException {
pds.setMaxPoolSize(maxPoolSize);
return this;
}
public OracleUcpDataSourceBuilder connectionWaitTimeout(int connectionWaitTimeout) throws SQLException {
pds.setConnectionWaitTimeout(connectionWaitTimeout);
return this;
}
public OracleUcpDataSourceBuilder inactiveConnectionTimeout(int inactiveConnectionTime) throws SQLException {
pds.setInactiveConnectionTimeout(inactiveConnectionTime);
return this;
}
public OracleUcpDataSourceBuilder maxIdleTime(int maxIdleTime) throws SQLException {
pds.setMaxIdleTime(maxIdleTime);
return this;
}
public PoolDataSource build() {
return pds;
}
}
Preferably, I would like to be able to apply the properties directly to the builder in one place. is this possible? what changes would I have to make?
Thanks...
Here is your builder, sir
public class OracleUcpDataSourceBuilder {
private Map<String, String> properties = new HashMap<String, String>();
private static final String[] REQ_PROPERTIES = new String[] {"username", "password", "URL"};
public static OracleUcpDataSourceBuilder create() {
return new OracleUcpDataSourceBuilder();
}
public DataSource build() {
for (String prop : REQ_PROPERTIES) {
Assert.notNull(properties.get(prop), "Property is required:" + prop);
}
PoolDataSource result = PoolDataSourceFactory.getPoolDataSource();
bind(result);
return result;
}
private void bind(DataSource result) {
MutablePropertyValues properties = new MutablePropertyValues(this.properties);
new RelaxedDataBinder(result).bind(properties);
}
public OracleUcpDataSourceBuilder URL(String url) {
this.properties.put("URL", url);
return this;
}
public OracleUcpDataSourceBuilder username(String username) {
this.properties.put("username", username);
return this;
}
public OracleUcpDataSourceBuilder password(String password) {
this.properties.put("password", password);
return this;
}
}
Just define a bean like this:
#Bean (name = "readOnlyDataSource")
#ConfigurationProperties(prefix = "datasource.readonly")
public DataSource getReadOnlyDataSource() {
return OracleUcpDataSourceBuilder.create().build();
}
Just make sure that the property names are correct. Spring will take care of the rest.
Note: I use DataSourceBuilder or Spring as a reference.. You can check it's source code also.
Edit: Added some methods to make sure some properties are configured. But this way, you need to set those properties manually to make sure that they're available.

Resources