I am working on a new resource adapter for Glassfish.
It uses a connection pool that has a property set in the admin console.
Connector Connection Pools -> Additional Properties -> name=url, value=127.0.0.1
I would like to read this property from the resource adapter.
(from my managed connection implementation class for example)
I tried checking the documentation and online examples but did not find out how to do it.
This is the common way for almost all web apps on j2ee containers with connection pools.
InitialContext ctx = new InitialContext();
//The JDBC Data source that we just created
DataSource ds = (DataSource) ctx.lookup("url here");
Connection connection = ds.getConnection();
#Connector(reauthenticationSupport = false, transactionSupport = TransactionSupport.TransactionSupportLevel.NoTransaction)
public class SocketResourceAdapter implements ResourceAdapter {
/** The logger */
private static Logger log = Logger.getLogger("SocketResourceAdapter");
/** Name property */
#ConfigProperty(defaultValue = "DefaultMessage", supportsDynamicUpdates = true)
private String name;
#ConfigProperty(defaultValue = "---", type = String.class)
private String url;
#ConfigProperty(type = Integer.class)
private Integer port;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
And then I can just use getUrl() in the resource adapter.
At first it did not work because I was setting the properties for the connection factory and not the resource adapter.
Related
I'm trying to retrieve secrets from vault using the AppRole authentication. But I get the error :
java.lang.IllegalArgumentException: URI is not absolute
What I do is create a vaultEndpoint then depending on the method choosen I use token authentication or AppRole authentication. There's no issue with the token authentication, however whenever I try to retrive a secret or even get the vaultToken to login with AppRole the URI is not absolute error occurs.
I 've seen in https://docs.oracle.com/javase/8/docs/api/java/net/URI.html that an URI is absolute when it specifies a scheme otherwise it is relative. But I think that my URI is specifing a scheme.
So I'm a bit lost here.
Does anyone know what I am doing wrong ? Or why I get this error ?
I use spring-vault-core-2.2.0.RELEASE
Here's my code :
VaultEndpoint ep = VaultEndpoint.create(host, portInt);
if (scheme != null) {
ep.setScheme(scheme);
}
if (authMethod.equals("token")) {
vaultTemplate = new VaultTemplate(ep, new TokenAuthentication(token));
} else if (authMethod.equals("appRole")) {
RestOperations restOperations = VaultClients.createRestTemplate();
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.roleId(AppRoleAuthenticationOptions.RoleId.provided(roleId))
.secretId(AppRoleAuthenticationOptions.SecretId.wrapped(VaultToken.of(secretId))).build();
vaultTemplate = new VaultTemplate(ep, new AppRoleAuthentication(options, restOperations));
}
}
I have the same error if I try to get the vaultToken :
RestOperations restOperations = VaultClients.createRestTemplate();
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.roleId(AppRoleAuthenticationOptions.RoleId.provided(roleId))
.secretId(AppRoleAuthenticationOptions.SecretId.wrapped(VaultToken.of(uncryptedSecretId))).build();
AppRoleAuthentication appRoleAuth = new AppRoleAuthentication(options, restOperations);
VaultToken appRoleToken = appRoleAuth.login();
Here is the error :
java.lang.IllegalArgumentException: URI is not absolute
at java.net.URI.toURL(Unknown Source)
at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:145)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:98)
at org.springframework.vault.client.VaultClients.lambda$createRestTemplate$0(VaultClients.java:128)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:742)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:586)
at org.springframework.vault.authentication.AppRoleAuthentication.getSecretId(AppRoleAuthentication.java:305)
at org.springframework.vault.authentication.AppRoleAuthentication.getAppRoleLoginBody(AppRoleAuthentication.java:344)
at org.springframework.vault.authentication.AppRoleAuthentication.createTokenUsingAppRole(AppRoleAuthentication.java:201)
at org.springframework.vault.authentication.AppRoleAuthentication.login(AppRoleAuthentication.java:191)
After further investigation, the issue was how I instanciated the restTemplate.
I added the spring context library to my project and implemented the AbstractVaultConfiguration class. This class contains a restOperations() function that solved my problem.
This is how I solved the issue :
public class AppRoleAuthenticationService extends AbstractVaultConfiguration {
private String roleId;
private String secretId;
private String host;
private String scheme;
private String port;
public AppRoleAuthenticationService(String roleId, String secretId, String host, String scheme, String port) {
this.roleId = roleId;
this.secretId = secretId;
this.host = host;
this.scheme = scheme;
this.port = port;
}
#Override
public VaultEndpoint vaultEndpoint() {
int portInt = Integer.parseInt(port);
VaultEndpoint ep = VaultEndpoint.create(host, portInt);
if (scheme != null) {
ep.setScheme(scheme);
}
return ep;
}
#Override
public ClientAuthentication clientAuthentication() {
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.roleId(AppRoleAuthenticationOptions.RoleId.provided(roleId))
.secretId(AppRoleAuthenticationOptions.SecretId.provided(secretId)).build();
return new AppRoleAuthentication(options, restOperations());
}
}
And then just use this class :
AppRoleAuthenticationService appRoleAuth = new AppRoleAuthenticationService(roleId,
uncryptedSecretId, host, scheme, port);
VaultEndpoint vaultEp = appRoleAuth.vaultEndpoint();
ClientAuthentication auth = appRoleAuth.clientAuthentication();
vaultTemplate = new VaultTemplate(vaultEp, auth);
Reading Pro Spring 5, an example that show Spring JDBC is using MySQL DB. I use the book source code. Here is the code
#Configuration
#PropertySource("classpath:db/jdbc2.properties")
#ComponentScan(basePackages = "com.apress.prospring5.ch6")
public class AppConfig {
private static Logger logger = LoggerFactory.getLogger(AppConfig.class);
#Value("${driverClassName}")
private String driverClassName;
#Value("${url}")
private String url;
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Bean(destroyMethod = "close")
public DataSource dataSource() {
try {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
} catch (Exception e) {
logger.error("DBCP DataSource bean cannot be created!", e);
return null;
}
}
jdbc2.properties
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/musicdb?useSSL=true
username=prospring5
password=prospring5
The test
public class AnnotationJdbcTest {
private GenericApplicationContext ctx;
private SingerDao singerDao;
#Before
public void setUp() {
ctx = new AnnotationConfigApplicationContext(AppConfig.class);
singerDao = ctx.getBean(SingerDao.class);
assertNotNull(singerDao);
}
#Test
public void testFindAll() {
List<Singer> singers = singerDao.findAll();
assertTrue(singers.size() == 3);
listSingers(singers);
ctx.close();
}
My MySQL instance already have the user prospring5 and the schema created and populated
When I try to run AnnotationJdbcTest, I get this exception:
Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Cannot create PoolableConnectionFactory (Access denied for user 'Mehdi'#'localhost' (using password: YES))
org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Cannot create PoolableConnectionFactory (Access denied for user 'Mehdi'#'localhost' (using password: YES))
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:82)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:612)
As you can see the project is using my computer name 'Mehdi' instead of the one in the .properties file 'prospring5' . Why is That? and how Can I fix it?
You can yourself download the source code and run it from here: https://github.com/Apress/pro-spring-5
the project is: chapter6/spring-jdbc-annotations
EDIT:
I printed the values as suggested by #STaefi and here is the output:
com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/musicdb?useSSL=false
Mehdi
prospring5
Code
#Bean()
public DataSource dataSource() {
try {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
System.out.println(driverClassName);
dataSource.setUrl(url);
System.out.println(url);
dataSource.setUsername(username);
System.out.println(username);
dataSource.setPassword(password);
System.out.println(password);
return dataSource;
First I tried setting the values at initialization and that was no good. but after I used username = "prospring5"; dataSource.setUsername(username); it worked. so what does this mean. why Spring cannot load the username like it successfully loaded the url and the password.
Could you please try by giving a default value as :
#Value("${driverClassName:com.mysql.cj.jdbc.Driver}")
private String driverClassName;
#Value("${url:jdbc:mysql://localhost:3306/musicdb?useSSL=true}")
private String url;
#Value("${username:prospring5}")
private String username;
#Value("${password:prospring5}")
private String password;
And remove the #PropertySource("classpath:db/jdbc2.properties") at the top. If this works then you can try with the propertySource again
So I have fixed the problem by simply using a different String identifier in the .propreties file . I changed it to one=prospring5 and it worked. Apparently using a proprety named 'username' will be loaded with anything other than Your Computer name. I don't know if Spring provides a list of prohibited values for property names. if they don't, they certainly should consider.
I have a fairly simple case where I am trying to add HTTP headers (not SOAP headers) to a request I am making using Spring's WebServiceTemplate.
I have defined a ClientInterceptor where I am doing:
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
try {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection connection = (HttpComponentsConnection) context.getConnection();
connection.addRequestHeader("Authorization", String.format("Bearer %s", someAccessToken));
} catch (IOException exception) {
// Do nothing
}
return true;
}
This is how I configure my SomeClient which extends WebServiceConfigurationSupport:
#Bean
public SomeClient someClient() {
...
SomeClientImpl service = new SomeClientImpl();
service.setObjectFactory(new com.path.ObjectFactory());
service.setDefaultUri(someUri);
service.setMarshaller(marshaller);
service.setUnmarshaller(marshaller);
service.setxStreamMarshaller(xStreamMarshaller);
service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor()});
service.setMessageSender(new HttpComponentsMessageSender());
service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor(), addHttpHeaderInterceptor()});
return service;
}
#Bean
public ClientInterceptor addHttpHeaderInterceptor() {
return new AddHttpHeaderInterceptor(someAccessToken);
}
#Bean
public Wss4jSecurityInterceptor wss4jSecurityInterceptor() {
Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
interceptor.setSecurementActions(securementAction);
interceptor.setSecurementUsername(securementUsername);
interceptor.setSecurementPassword(securementPassword);
interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
interceptor.setSecurementMustUnderstand(false);
return interceptor;
}
But the Authorization header is not being added. I have also tried with a CustomMessageCallback:
public class CustomMessageCallback implements WebServiceMessageCallback {
private String headerKey;
private String headerValue;
public CustomMessageCallback(String headerKey, String headerValue) {
this.headerKey = headerKey;
this.headerValue = headerValue;
}
#Override
public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection conn = (HttpComponentsConnection) context.getConnection();
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
}
But it does not seem to work as well. What am I doing wrong, why the Authorization header is not being added? Thanks!
Use the HeadersAwareSenderWebServiceConnection interface instead of the actual underlying connection.
TransportContext context = TransportContextHolder.getTransportContext();
HeadersAwareSenderWebServiceConnection connection = (HeadersAwareSenderWebServiceConnection) context.getConnection();
connection.addRequestHeader("Authorization", String.format("Bearer %s", "********"));
Now if you upgrade/switch HTTP library you don't need to change this code.
To answer your question about what you are doing wrong is that you are casting to the wrong class. Yes the class you are using is deprecated but it is part of the library you are using, you cannot just cast to a different class without changing the underlying HTTP library.
What I did in past is to use a WebServiceMessageCallback like this one:
public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
private String headerKey;
private String headerValue;
private String soapAction;
public WsHttpHeaderCallback(String headerKey, String headerValue, String soapAction)
{
super();
this.headerKey = headerKey;
this.headerValue = headerValue;
this.soapAction = soapAction;
validateRequiredFields();
}
public WsHttpHeaderCallback()
{
super();
}
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
validateRequiredFields();
addRequestHeader(headerKey, headerValue);
if (StringUtils.hasText(this.soapAction))
{
AxiomSoapMessage axiomMessage = (AxiomSoapMessage) message;
axiomMessage.setSoapAction(this.soapAction);
}
}
private void validateRequiredFields()
{
if( !StringUtils.hasText(headerKey) )
{
throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con chiave non valida: ["+headerKey+"]");
}
if( !StringUtils.hasText(headerValue) )
{
throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con valore non valido: ["+headerValue+"]");
}
}
private void addRequestHeader(String headerKey, String headerValue)
{
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpComponentsConnection)
{
HttpComponentsConnection conn = (HttpComponentsConnection) connection;
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
else if( connection instanceof ClientHttpRequestConnection )
{
ClientHttpRequestConnection conn = (ClientHttpRequestConnection)connection;
conn.getClientHttpRequest().getHeaders().add(headerKey, headerValue);
}
}
}
Then I used it in this way:
wsTemplate.marshalSendAndReceive(wsUrl, request, new WsHttpHeaderCallback(headerKey, headerValue, soapAction) );
In this way I successfully set all the needed HttpHeaders (in my case just one :) )
I hope it is usefull
Angelo
TL;DR
Your messageSender should be an instance of HttpComponentsMessageSender instead of HttpUrlConnectionMessageSender. Also you need to provide proper credentials.
getConnection() function of TransportContext returns an implementation of WebServiceConnection. Both HttpUrlConnection and HttpComponentsConnection are implementations of the same. So basically you are getting the wrong type of connection,hence the ClassCastException.
The ClientInterceptor will work for custom headers but not for Authorization header. For that, your HttpComponentsMessageSender needs to be configured with your credentials.
The proper configuration should be like this
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Bean
public SomeClient someClient() {
SomeClientImpl service = new SomeClientImpl();
service.setMessageSender();
//other configs
return service;
}
public HttpComponentsMessageSender getMessageSender(){
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
httpComponentsMessageSender.setCredentials(getCredentials);
}
public UsernamePasswordCredentials getCredentials(){
return new UsernamePasswordCredentials(username, password);
}
I went through a similar exercise, for an endpointInterceptor the connection returns a HttpServletConnection. Therefore used the following and managed to get the HTTP headers added.
HttpServletConnection connection = (HttpServletConnection)context.getConnection();
HttpServletResponse response = connection.getHttpServletResponse();
HttpServletRequest request = connection.getHttpServletRequest();
response.addHeader("myheader", "myvalue");
Some additional tips:
If you want to send back the same header you received in the request, use following in the handleResponse method of the endpointInterceptor
response.addHeader("myheader", request.getHeader("myheader"));
If you are trying to add custom headers in an clientInterceptor to send to a downstream use below in the handleRequest method,
HttpUrlConnection connection = (HttpUrlConnection)context.getConnection();
connection.addRequestHeader("myheader", "myvalue");
Spring JDBC allows to specify in properties file for PROD:
jdbc.driverClassName = oracle.jdbc.OracleDriver
jdbc.url = jdbc:oracle:thin:#...
and for tests
jdbc.driverClassName = org.h2.Driver
jdbc.url = jdbc:h2:mem:test;INIT=...
Thus it's possible to instantiate needed java.sql.DataSource instance depends of configuration settings with generic code
#Bean
public DataSource dataSource(
#Value("${jdbc.driverClassName}") String driverClass,
#Value("${jdbc.url}") String url,
#Value("${jdbc.username}") String username,
#Value("${jdbc.password}") String password
) {
DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
dataSource.setDriverClassName(driverClass);
return dataSource;
}
Is it possible in Spring to configure specific type of java.jms.ConnectionFactory via driver and URL properties' strings like in Spring JDBC?
Actually, my goal is to use Tibco connection factory for PROD and ActiveMQ for tests.
You can use spring profiles to pull in a different Bean for tests or you can simply override the connection factory bean with a different one in the test case.
EDIT
#Bean
public FactoryBean<ConnectionFactory> connectionFactory(#Value("${jms.url}") final String urlProp) {
return new AbstractFactoryBean<ConnectionFactory>() {
#Override
public Class<?> getObjectType() {
if (urlProp.startsWith("activemq:")) {
return ActiveMQConnectionFactory.class;
}
// ...
throw new RuntimeException("bad url: " + urlProp);
}
#Override
protected ConnectionFactory createInstance() throws Exception {
if (urlProp.startsWith("activemq:")) {
URI uri = new URI(urlProp.substring(urlProp.indexOf(":") + 1));
return new ActiveMQConnectionFactory(uri);
}
// ...
throw new RuntimeException("bad url: " + urlProp);
}
};
}
and
jms.url=activemq:vm://localhost
I am working on a client server application. I was using Restlet 2.0.3. Due to a heavy load task my client was getting timed-out. I searched on the forum and found that switching over to Restlet 2.2 would help. So I did that. I upgraded my Restlet to 2.2.1. But now my code has stopped working at precisely this method.
public synchronized UUID generateUniqueSessionId(String userAtDomain)
{
UUID newSessionId = UUID.randomUUID();
SessionAttributes sessionAttributes = new SessionAttributes();
sessionAttributes.setAlive(true);
sessionAttributes.setFQUserName(userAtDomain);
loggedInUsers.put(newSessionId, sessionAttributes);
return newSessionId;
}
So I am returning the UUID at last.
This code is on the server and invoked during login. Following is the error that I am getting from the logs.
16 Mar 2015 11:23:18 WARN - Unable to find a converter for this object : f3d2edda-443c-454d-856a-fb4e7ed9c535
And this object referred in the log belongs to java.util.UUID
The code on the client side which invokes the server looks like this.
public UUID authenticateUser(String username, String passwd) {
try {
String url = RESTLetWebSvcsFactory.getFactoryInstance().getServer_URL() + "login/" + username + "/" + passwd;
Context context = new Context();
Client client = new Client(context, Protocol.HTTP);
ClientHelper helper = new ClientHelper(client);
helper.getHelpedParameters().set("socketConnectTimeoutMs", "60000");
ClientResource cr = new ClientResource(url);
LoginLogoutResource resource = cr.wrap(LoginLogoutResource.class);
return resource.loginUser();
} catch (ResourceException re) {
if (re.getStatus().isConnectorError()) {
try {
RESTLetWebSvcsFactory.enableFallBackServer();
String url = RESTLetWebSvcsFactory.getFactoryInstance().getServer_URL() + "login/" + username + "/" + passwd;
ClientResource cr = new ClientResource(url);
LoginLogoutResource resource = cr.wrap(LoginLogoutResource.class);
return resource.loginUser();
} catch (ResourceException re1) {
int statusCode = new RESTLetErrorHandler().handleServerError(re);
if (statusCode != -1) {
throw new UserCRUDException(statusCode);
}
}
} else {
throw new UserCRUDException(new RESTLetErrorHandler().handleServerError(re));
}
}
return null;
}
Note: USERCRUDException is my own exception and not one of JAVA
Please help me resolve this problem which probably prevents returning the UUID from the server and thus my application isn't moving ahead.
Thanks in advance
Restlet uses under the hood a generic bean conversion to convert bean to representation (and representation to bean). So your problem depends on the converter you use.
I made a test with the extension org.restlet.ext.jackson (that contains such bean converter) and with version 2.2.1 of Restlet. My test bean is described below:
public class TestBean {
private String name;
private String message;
private UUID uuid;
(...)
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
}
and the following test server resource:
public class MyServerResource extends ServerResource {
#Get
public TestBean ping() {
TestBean bean = new TestBean();
bean.setMessage("pong");
bean.setUuid(UUID.randomUUID());
return bean;
}
}
I received the following content for a GET method:
{
"name":"my name",
"message":"my message",
"uuid":"be9e5381-d5c9-4e45-b5c8-4af1f8bdca16"
}
Can you provide the converter you use? and the list of the Restlet dependencies you have in your application?
Hope it helps you,
Thierry