saslConfig using spring rabbitmq not working - rabbitmq

My project needed SSL authentication mechanism to be EXTERNAL(using SSL certificates only and avoiding username/password on rabbitmq). For the connectionfactory bean we gave property name="saslConfig" value= "DefaultSaslConfig.EXTERNAL", but we are getting an error: "Cannot convert value of type [java.lang.String] to required type [com.rabbitmq.client.SaslConfig] for property 'saslConfig': no matching editors or conversion strategy found". we tried other values like value= "com.rabbitmq.client.DefaultSaslConfig.EXTERNAL" and value="EXTERNAL", but still the error persists. Can you please check on the configuration and logs below and provide me your suggestions.
Bean configuration
<rabbit:connection-factory id="connectionFactory" connection-factory="clientConnectionFactory" host="x.y.z.m" port="5671"/>
<bean id="clientConnectionFactory" class="org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean">
<property name="useSSL" value="true" />
<property name="saslConfig" value=com.rabbitmq.client.DefaultSaslConfig.EXTERNAL"/>
<property name="sslPropertiesLocation" value="classpath:/rabbitSSL.properties"/></bean>
Logs
Caused by: java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.rabbitmq.client.SaslConfig] for property 'saslConfig': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:306)
at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:576)

The following worked for me (source: https://github.com/spring-projects/spring-boot/issues/6719#issuecomment-259268574):
#PostConstruct
public void init() {
if (rabbitProperties.getSsl().isEnabled() && rabbitProperties.getSsl().getKeyStore() != null) {
cachingConnectionFactory.getRabbitConnectionFactory().setSaslConfig(DefaultSaslConfig.EXTERNAL);
}
}

EXTERNAL is a static variable, not an enum.
Use
"#{T(com.rabbitmq.client.DefaultSaslConfig).EXTERNAL}"
which is a SpEL expression using the type operator (T) to get a reference to the static.
See SpEL

We can use ConnectionFactoryCustomizer in package org.springframework.boot.autoconfigure.amqp
to set sasl config on rabbitmq connection factory on bean creation.
#Autowired
RabbitProperties rabbitProperties
#Bean
ConnectionFactoryCustomizer connectionFactoryCustomizer() {
if (rabbitProperties.getSsl().getEnabled() && rabbitProperties.getSsl().getKeyStore() != null) {
return (connectionFactory) -> connectionFactory.setSaslConfig(DefaultSaslConfig.EXTERNAL)
}
return (connectionFactory) -> connectionFactory.setSaslConfig(DefaultSaslConfig.PLAIN)
}

Related

How to configure CXF servlet in Springboot 2.1.1 Final?

PLease find the error I am facing :
In springboot 2.1.1 I am getting below error :
APPLICATION FAILED TO START
Description: Parameter 1 of constructor in
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
required a bean of type
'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath'
that could not be found.
The following candidates were found but could not be injected:
- Bean method 'dispatcherServletRegistration' in 'DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration'
not loaded because DispatcherServlet Registration found non dispatcher
servlet dispatcherServlet
Action:
Consider revisiting the entries above or defining a bean of type
'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath'
in your configuration.
My configuration:
#Configuration
public class CXFConfig {
#Bean
public ServletRegistrationBean dispatcherServlet() {
final ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new CXFCdiServlet(), "/services/*");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus springBus = new SpringBus();
springBus.getInInterceptors().add(new AppInboundInterceptor());
springBus.getOutInterceptors().add(new AppOutboundInterceptor());
return springBus;
}
}
Please confirm how to do the configuration?
dispatcherServlet() method doesn't work in Springboot 2.1.1
I solved this problem by change the method name from dispatcherServlet to disServlet.
Maybe you can try.

AbstractMethodError with jTDS JDBC Driver on Tomcat 8

I am deploying a web app (WAR) to a Tomcat 8 web container.
The WAR includes in the '/WEB-INF/lib' directory the following jTDS JDBC driver:
<dependency org="net.sourceforge.jtds" name="jtds" rev="1.3.1" />
(file is: jtds-1.3.1.jar).
This is how the resource is defined in META-INF/context.xml:
<Resource name="jdbc/jtds/sybase/somedb"
auth="Container"
type="javax.sql.DataSource"
driverClassName="net.sourceforge.jtds.jdbc.Driver"
url="jdbc:jtds:sybase://localhost:2501/somedb"
username="someuser" password="somepassword"
/>
In my code I obtain the javax.sql.DataSource the normal way:
InitialContext cxt = new InitialContext();
if ( cxt == null ) {
throw new RuntimeException("Uh oh -- no context!");
}
DataSource ds = (DataSource) cxt.lookup( lookupName );
I further verify (by printing) that the DataSource object ds is of the expected type:
org.apache.tomcat.dbcp.dbcp2.BasicDataSource
… but when I try to get a connection out of it:
Connection conn = ds.getConnection();
… I get the following trace:
java.lang.AbstractMethodError
net.sourceforge.jtds.jdbc.JtdsConnection.isValid(JtdsConnection.java:2833)
org.apache.tomcat.dbcp.dbcp2.DelegatingConnection.isValid(DelegatingConnection.java:924)
org.apache.tomcat.dbcp.dbcp2.PoolableConnection.validate(PoolableConnection.java:282)
org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory.validateConnection(PoolableConnectionFactory.java:359)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.validateConnectionFactory(BasicDataSource.java:2316)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:2299)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.createDataSource(BasicDataSource.java:2043)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.getConnection(BasicDataSource.java:1543)
What gives?
Turns out I had to add:
validationQuery="select 1"
in the Resource declaration in context.xml.
This is mentioned here (although mispelled as validateQuery).
Digging into the implementation of JtdsConnection one sees:
/* (non-Javadoc)
* #see java.sql.Connection#isValid(int)
*/
public boolean isValid(int timeout) throws SQLException {
// TODO Auto-generated method stub
throw new AbstractMethodError();
}
This is really weird, I think AbstractMethodError is supposedly thrown by the compiler only, unimplemented methods ought to throw UnsupportedOperationException. At any rate, the following code from PoolableConnection shows why the presence or not of validationQuery in context.xml can change things. Your validationQuery is passed as the value of the sql String parameter in the below method (or null if you don't define a validationQuery):
public void More ...validate(String sql, int timeout) throws SQLException {
...
if (sql == null || sql.length() == 0) {
...
if (!isValid(timeout)) {
throw new SQLException("isValid() returned false");
}
return;
}
...
}
So basically if no validationQuery is present, then the connection's own implementation of isValid is consulted which in the case of JtdsConnection weirdly throws AbstractMethodError.
The answer mentioned above by Marcus worked for me when I encountered this problem. To give a specific example of how the validationQuery setting looks in the context.xml file:
<Resource name="jdbc/myDB" auth="Container" type="javax.sql.DataSource"
driverClassName="net.sourceforge.jtds.jdbc.Driver"
url="jdbc:jtds:sqlserver://SQLSERVER01:1433/mydbname;instance=MYDBINSTANCE"
username="dbuserid" password="dbpassword"
validationQuery="select 1"
/>
The validationQuery setting goes in with each driver setting for your db connections. So each time you add another db entry to your context.xml file, you will need to include this setting with the driver settings.
The above answer works. If you are setting it up for standalone Java application, set the validation query in the datasource.
BasicDataSource ds = new BasicDataSource();
ds.setUsername(user);
ds.setPassword(getPassword());
ds.setUrl(jdbcUrl);
ds.setDriverClassName(driver);
ds.setMaxTotal(10);
ds.setValidationQuery("select 1"); //DBCP throws error without this query

SessionFactory XML configuration works, but not Java configuration. Why?

In a Spring XML configuration, I have the followings:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
....
</bean>
and in a Java class, I have
#Autowired
private SessionFactory sessionFactory;
without a setter. That works.
Now, I change the sessionFactory to a Java configuration as the followings.
#Configuration
#EnableTransactionManagement
#PropertySource({ "classpath:jdbc.properties" })
public class PersistenceConfig {
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder lsfb = new LocalSessionFactoryBuilder(dataSource());
lsfb.addAnnotatedClasses(...);
lsfb.setProperties(hibernateProperties());
return lsfb.buildSessionFactory();
}
// ...
}
And I get an error "could not autowire field". Adding a setter doesn't help. Why the sessionFactory can't get autowired with a Java configuration?
BTW, I can work around this problem by having a Java configuration for the DAO as well.
I see that there is no #ComponentScan annotation on your #Configuration class, so probably the problem is in how you import this configuration. Please ensure that all particular beans live in the same context or at least that PersistenceConfig is parent to the context in which you are autowiring SessionFactory
I have added the #ComponentScan annotation and it doesn't solve the problem. The annotation tells Spring to look for any #Components to configure as beans. This problem seems to me is that during the process of creating a bean with #Component, it can't find a bean configured in my Java configuration file which is started in WebAppInitializer.

How to set a spring.NET AOP advice before method call

I want to intercept a method call before execution using spring.NET. Let's assume the class/method to be intercepted is:
public class Listener
{
public void Handle()
{
// method body
}
}
This is what I've done (assuming all code is in a namespace called Example):
1.Created the advice:
public class MyAopAdvice : IMethodBeforeAdvice
{
public void Before(MethodInfo method, object[] args, object target)
{
// Advice action
}
}
2.Updated my spring xml configs:
<object id="myAopAdvice" type="Example.MyAopAdvice" />
<object id="listener" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="Target">
<object type="Example.Listener" autowire="autodetect"/>
</property>
<property name="InterceptorNames">
<list>
<value>myAopAdvice</value>
</list>
</property>
</object>
For some reason my Advice code is not getting hit if I put a breakpoint in it. However, if I add some console logging statements within my advice, it seems they are logged, but not at the appropriate time (i.e., before calling Listener.Handle()).
I'm willing to bet my configs are wrong (for once, I may be missing a way to tell the configs to listen for just the Handle method call and not any other method that Listener may have). Any ideas what's wrong?
Declare your Handle method as virtual:
public virtual void Handle() // ...
Your class does not implement any interfaces, which spring.net's default aop mechanism uses to create proxies. When spring.net does not find any interfaces to proxy, it looks for virtual methods to create a proxy for a class.

Apache - CXF jaxrs-server - unable to hit the resource which is defined first in jaxrs-server endpoint

I'm using Apache-CXF for JAX-RS implementation. I have two resources which are defined in two bean. My jaxrs-server in context.xml os as follow
<jaxrs:server id="serverId" address="/">
<jaxrs:serviceBeans>
<bean id="bean1" class="com.Bean1" />
<bean id="bean2" class="com.Bean2" />
</jaxrs:serviceBeans>
</jaxrs:server>
Interface for Bean1 is as follows -
#Path("/")
public interface IBean1 {
#GET
#Path("/beaninfo1")
#Produces({ MediaType.APPLICATION_XML })
public Response checkBean1();
}
Interface for Bean2 is as follows -
#Path("/")
public interface IBean2 {
#GET
#Path("/beaninfo2")
#Produces({ MediaType.APPLICATION_XML })
public Response checkBean1();
}
I'm unable to hit the resource which is defined in last in serviceBans definition. In this case i'm able to hit Bean2 but not Bean1, getting 404 error, where as if i put Bean2 first and then Bean1, i'm able to hit Bean1 only.
Is there anything wrong with my configuration ?
It is possible to have the same #Path annotation at class level. You need to use a resourcecomparator. Please check this question
Yes. Give them different #Path annotations at the class level.