Not working - Programmatic approach for CXF's Circuit-Breaker feature - cxf

As per the documentation - https://cwiki.apache.org/confluence/display/CXF20DOC/JAX-RS+Failover#JAX-RSFailover-Code.1, I tried running the following code along with associated configurations, but the circuit breaker mechanism is not opening up once the threshold count for connectivity failures have exceeded. As the circuit stays closed, the invocation attempts are still being accepted which is against expected behaviour.
public class CustomerRestClient {
private CustomerRestClientFactory customerRestClientFactory;
public List<Customer> filterByFirstName(String firstName) {
List<Customer> filteredCustomers = new ArrayList<>();
CircuitBreakerFailoverFeature cbFailoverFeature = new CircuitBreakerFailoverFeature(4, 180000L);
SequentialStrategy strategy = new SequentialStrategy();
cbFailoverFeature.setStrategy(strategy);
List<Feature> featureList = new ArrayList<Feature>();
featureList.add(cbFailoverFeature);
WebClient client = customerRestClientFactory.getClient(featureList).path("/");
// Call service to get all customers
List<Customer> customers = client.get(new GenericType<List<Customer>>() {});
return filteredCustomers;
}
public void setCustomerRestClientFactory(CustomerRestClientFactory customerRestClientFactory) {
this.customerRestClientFactory = customerRestClientFactory;
}
}
public class CustomerRestClientFactory implements InitializingBean {
private List providerList; // Value is injected by Spring
private String serviceUrl; // Value is injected by Spring
public WebClient getClient(List<? extends Feature> featureList) {
if (featureList == null || featureList.isEmpty()) {
throw new IllegalArgumentException("featureList is not initialized.");
}
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(serviceUrl);
bean.setServiceClass(WebClient.class);
bean.setProviders(providerList);
bean.setFeatures(featureList);
return bean.createWebClient();
}
}
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat"> <constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ss"/>
</bean>
</property>
<property name="serializationInclusion">
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>
</bean>
<bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider">
<property name="mapper" ref="objectMapper"/>
</bean>
<util:list id="providerList">
<ref bean="jsonProvider" />
<bean name="exceptionHandler" class="com.mycompany.refapp.exception.AppExceptionHandler" />
</util:list>
<bean id="customerRestClientFactory" class="com.mycompany.refapp.client.CustomerRestClientFactory">
<property name="providerList" ref="providerList" />
<property name="serviceUrl" value="${customer.rest.service.url}" />
</bean>
Log containing stack traces (available here).
After a lot of debugging, I came to an understanding that the counter for connection failures never exceeds the threshold limit because the state of the data (including the counter) is specific to each of the WebClient objects, instantiated for each call. I assumed that if the same instance of a WebClient is used across multiple calls that fail, then the counter would have been updated and eventually open the circuit. Please find attached screenshot for details.
I would like to get a second opinion on whether my understanding is correct.

You have opened issue https://issues.apache.org/jira/browse/CXF-7663 in parallel, so I share the response from Colm here:
If you want to use the Circuit-Breaker feature then you need to use
the same Webclient instance for all of the invocations - it won't work
if you are creating a new WebClient per-call. Here's a test that shows
how it works:
https://github.com/coheigea/testcases/blob/218044e3126cff9339e27a69cd8d3c5f3fe308ea/apache/cxf/cxf-failover/src/test/java/org/apache/coheigea/cxf/failover/feature/FailoverTest.java#L90

Related

QPID Connection factory properties

My system is based on camel and use Apache QPID 0.37.0 to consume messages from a remote AMQPS sever. Our system authenticates via a client certificate. Thus I have this piece of configuration:
<bean id="jmsConnectionFactory" class="org.apache.qpid.jms.JmsConnectionFactory">
<constructor-arg name="remoteURI" value="amqps://some-location:5671?transport.keyStoreLocation=/very/long/path/nnn-openssl.p12&transport.keyStorePassword=*******&transport.trustStoreLocation=/very/long/path/server.keystore&transport.trustStorePassword=*******"/>
</bean>
This is just working fine. However, configuring key/trust store this way (i.e. in the URI) has several drawbacks:
First, it is not easy to read and maintain.
Some components log the URI, so the paths (I can live with it) and the passwords (ouch...) get logged.
I know it's possible to configure via a system property (javax.net.ssl.keyStore, and son on), but it's not an option because different modules may use different key and trust store, and we want to keep them in separate files.
Is there a way to configure those transport properties of JmsConnectionFactory in a different way ?
Something like:
<bean id="jmsConnectionFactory" class="org.apache.qpid.jms.JmsConnectionFactory">
<constructor-arg name="remoteURI" value="amqps://some-location:5671"/>
<property name="transport.keyStoreLocation" value="/very/long/path/nnn-openssl.p12"/>
...
</bean>
Note that this factory is used in a JMSConfig, which in turn is used within an AMQPComponent:
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration" >
<property name="connectionFactory" ref="jmsConnectionFactory" />
...
</bean>
<bean id="amqp" class="org.apache.camel.component.amqp.AMQPComponent">
<property name="testConnectionOnStartup" value="true"/>
<property name="configuration" ref="jmsConfig" />
...
</bean>
The short answer is no, that's all that class supports. However you could write a configuration bean which has the properties you require, and have that create the bean. Off the top of my head, something like this:
#Configuration
public class QpidConfiguration {
// Add setters for these
private String host;
private int port = 5671;
private String keyStore;
private String keyStorePassword;
private String trustStoreLocation;
private String trustStorePassword;
#Bean
public JmsConnectionFactory createConnectionFactory() {
return new JmsConnectionFactory("amqps://" + host" + ":" + port + "?transport.keyStoreLocation=" + keyStoreLocation + "&transport.keyStorePassword=" + keyStorePassword + "&transport.trustStoreLocation=" + trustStoreLocation + "&transport.trustStorePassword=" + trustStorePassword);
}
}
You probably want to add some parameter validation in there to be safe.
You can then define the bean using more convenient properties.
<bean id="jmsConnectionFactory" class="com.example.QpidConfiguration">
<property name="host" value="some-location"/>
<property name="keyStoreLocation" value="/very/long/path/nnn-openssl.p12"/>
...
</bean>
You can also leverage encrypted property placeholders, if your heart so desires.

Jackson ignores ACCEPT_EMPTY_STRING_AS_NULL_OBJECT [duplicate]

This question already has an answer here:
Jackson ObjectMapper DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
(1 answer)
Closed 6 years ago.
I need to treat the empty string ("") as null in Spring MVC. This because I found that when I edit a form field (with Angular's ng-model) that was originally undefined, then erase it to blank, Angular will send that bean property as empty string. If I don't touch such null value, it won't be sent in the JSON payload and thus treated as null.
I have configured Jackson with the ACCEPT_EMPTY_STRING_AS_NULL_OBJECT DeserializationFeature in my Spring context. Configuration below:
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<constructor-arg>
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="featuresToDisable">
<array>
<util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS" />
</array>
</property>
<property name="featuresToEnable">
<array>
<util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT" />
<util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.WRITE_ENUMS_USING_TO_STRING" />
<util:constant static-field="com.fasterxml.jackson.databind.DeserializationFeature.READ_ENUMS_USING_TO_STRING" />
<util:constant static-field="com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT" />
</array>
</property>
<property name="serializationInclusion">
<util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL" />
</property>
</bean>
</constructor-arg>
<property name="supportedMediaTypes">
<array>
<value>application/json</value>
</array>
</property>
</bean>
Still, it looks like it decodes empty strings as empty strings rather than null. I have debugged and I could see "" in the payload. Here is a sample from an admin page
{
"id":"ADMIN.NOTIFIER_SMTP",
"host":"aa.bb.cc",
"protocol":"POP",
"securityProtocol":"PLAIN",
"username":"", <----- shall be null on server, I don't care about client
"passwordSet":false
}
Question is: is something wrong in my configuration? How do I tell Jackson to treat empty as null?
May be you want to write a CustomObjectMapper
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="custom.CustomObjectMapper"/>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
and add above deserializer in it.
Jackson will give you null for other objects, but for String it will give empty String.
But you can use a Custom JsonDeserializer to do this:
class CustomDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
String node = jsonParser.readValueAs(String.class);
if (StringUtils.isBlank(node)) {
return null;
}
return node;
}}
In class you have to use it for location field:
class EventBean {
public Long eventId;
public String title;
#JsonDeserialize(using = CustomDeserializer.class)
public String location;
}

Apache Camel Multicast notifyBuilder fails

Of some reason, QUEUE_A does not ALWAYS has 1 exchanges - sometimes it has 0, unless I add a Thread.sleep(100) to the test. I guess whenCompleted/whenDone isn't completely done when it actually says it's done. How can I verify that it is completely done?
multicast().parallelProcessing().to(QUEUE_A, QUEUE_B, QUEUE_C, QUEUE_D)
And testing with:
#Test
public void test() {
NotifyBuilder notify = new NotifyBuilder(context)
.from(QUEUE_INCOMING)
.whenCompleted(1)
.create();
template.sendBody(QUEUE_INCOMING, streamToString(loadResourceAsStream("/data/TestData.xml")));
boolean matches = notify.matches(4, SECONDS);
assertTrue("Notify failed", matches);
Thread.sleep(100); //Without this, it fails
verifyEndpoints(1, context, QUEUE_A, QUEUE_B, QUEUE_C, QUEUE_D);
}
public static void verifyEndpoints(int expectedSize, ModelCamelContext context, String... endpoints) {
for (String endpoint : endpoints) {
BrowsableEndpoint be = context.getEndpoint(endpoint, BrowsableEndpoint.class);
assertThat(String.format("Endpoint exchanges '%s' has wrong size", endpoint), be.getExchanges(), hasSize(expectedSize));
}
}
And the endpoint bean, using ActiveMQ when testing, but is going to use WebSphere MQ in prod:
<bean id="wmq" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false" />
</bean>
</property>
</bean>
The problem is that you browse a WMQ asap after sending a message to it, and therefore depending on broker implementation and timing etc, you may not see the very last messages, when using the JMS browsing api.
And hence why it seems to fix when you wait a bit with the sleep.

Spring #Transactional and PROPAGATION_REQUIRES_NEW with sql server 2012

I have a service that has several methods marked with #Transactional, including methods a, b and c. These 3 methods are nested in the following way a -> b -> c. Here is a code sample:
#Transactional
public void a() {
while(condition) {
try {
b();
} catch(MyException e) {
logger.warn(e.getMessage());
}
}
}
Method b however is annotated like this:
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = {MyException.class})
However, when MyException is thrown from method c, the exception is caught at method a and when that exits, the transaction is comitted and that includes whatever has been done by call to b() that threw the exception and should have been rolled back(?).
I am using sql server 2012 express with spring 3.0.7 and my spring configuration is like this:
<tx:annotation-driven />
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<qualifier value="txm1"/>
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/jpa-persistence.xml"/>
<property name="persistenceUnitName" value="Unit1" />
</bean>
Method b seems to be in the same class as method a. If you're not using AspectJ, the #Transactional-annotations are handled by a JDK dynamic proxy, through which your calls from outside the class travel to method a. The call needs to travel through the proxy for the #Transactional-annotations to have effect, see for example here, under 'Understanding AOP proxies': http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch08s06.html
The key thing to understand here is that the client code inside the
main(..) of the Main class has a reference to the proxy. This means
that method calls on that object reference will be calls on the proxy,
and as such the proxy will be able to delegate to all of the
interceptors (advice) that are relevant to that particular method
call. However, once the call has finally reached the target object,
the SimplePojo reference in this case, any method calls that it may
make on itself, such as this.bar() or this.foo(), are going to be
invoked against the this reference, and not the proxy. This has
important implications. It means that self-invocation is not going to
result in the advice associated with a method invocation getting a
chance to execute.

Spring Security:password encoding in DB and in applicationContext

Have config (applicationContext-security.xml):
<authentication-manager alias="authenticationManager">
<authentication-provider>
<password-encoder hash="sha"/>
<jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>
</authentication-manager>
from other side have SQLs from my dataSource(it's JdbcDaoImpl):
...
public static final String DEF_USERS_BY_USERNAME_QUERY =
"select username,password,enabled " +
"from users " +
"where username = ?";
...
There is now word about sha in this code,so password selected from standard Spring Security users table not encoded.
Perhaps, I should provide some sha attribute for password column in my hibernate mapping config here:
<class name="model.UserDetails" table="users">
<id name="id">
<generator class="increment"/>
</id>
<property name="username" column="username"/>
<property name="password" column="password"/>
<property name="enabled" column="enabled"/>
<property name="mail" column="mail"/>
<property name="city" column="city"/>
<property name="confirmed" column="confirmed"/>
<property name="confirmationCode" column="confirmation_code"/>
<set name="authorities" cascade="all" inverse="true">
<key column="id" not-null="true"/>
<one-to-many class="model.Authority"/>
</set>
</class>
For now password saved to DB as is,but should be encoded.
How to friend applicationContext config and DB queries to be the same password encoding?
If you are choosing a hashing system yourself, rather than building an app using an existing database which already contains hashed passwords, then you should make sure your hashing algorithm also uses a salt. Don't just use a plain digest.
A good choice is bcrypt, which we now support directly in Spring Security 3.1 via the BCryptPasswordEncoder (implemented using jBCrypt). This automatically generates a salt and concatenates it with the hash value in a single String.
Some databases have built-in support for hashing (e.g. Postgres). Otherwise, you need to hash the password yourself before passing it to JDBC:
String password = "plaintextPassword";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);
That's all you need to do to encode the passwords when you create a user.
For authentication, you would use something like:
<bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<bean id="authProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="yourJdbcUserService" />
<property name="passwordEncoder" ref="encoder" />
</bean>
A little more explanation on the accepted answer. Hope it helps someone.
Hash the password yourself before putting it to database:
String password = "plaintextPassword";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);
Add BCryptPasswordEncoder bean to your security-config.xml
Add passwordEncoder as a property to Authentication Provider class. Autowire it or provide setter and getter methods.
#AutoWired
private BCryptPasswordEncoder passwordEncoder;
Get the property while you authendicate user for login
<bean id="dbAuthenticationProvider" class="mypackage.auth.spring.DBAuthenticationProvider" >
<property name="dataSource" ref="routingDataSource"></property>
<property name="passwordEncoder" ref="encoder" />
<property name="passwordQuery"
value="select password as password from tbl where username=:username">
</property>
</bean>
And in the authenticating class match both passwords
new BCryptPasswordEncoder().matches(plainTextPasswdFromUserInput, hashedPasswdFromDb)
In a simple way can you do something like in applicationContext-security.xml
<authentication-manager alias="authenticationManager">
<authentication-provider>
<password-encoder ref="encoder"/>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
select username,password, enabled
from principal where username=?"
authorities-by-username-query="
select p.username, a.authority from principal p, authority a
where p.id = a.principal_id and p.username=?"
/>
</authentication-provider>
</authentication-manager>
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
In Java
public static String encodePasswordWithBCrypt(String plainPassword){
return new BCryptPasswordEncoder().encode(plainPassword);
}
Then test it
System.out.println(encodePasswordWithBCrypt("fsdfd"));
Using Spring Security 3.1, try this:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="service">
<password-encoder hash="sha"/>
<jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="dataSource" ...>
...
</beans:bean>
<beans:bean id="service" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
...
</beans:bean>
What's new: authentication-provider points to service and service points to datasource.
Edit: In Java you will have to encode the password with something like this:
DigestUtils.sha(request.getParameter("password"));
Warn: Be careful! Do not mix SHA with MD5!
If you set the password-encoder of the authentication-provider as SHA, you need to encode in Java the same way to keep consistent. But if you enconde in Java as MD5, as the sample you found, do not forget to set the hash to "md5". DigestUtils also provides md5 encoder:
DigestUtils.md5(request.getParameter("password"));
Just a tip for doing it with annotations
#Configuration
#EnableWebSecurity
#PropertySource("classpath://configs.properties")
public class SecurityContextConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
#Bean(name = "passwordEncoder")
public PasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
The accepted answer is right.
I tested it with spring 3.1 and BCrypt encode algorithm.
When create a user.
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));
userDao.save(userEntity);
When the user login, Remember, use the plain password (not hashed). just like:
Authentication request = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
Here is security-config:
<bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userService" />
<property name="hideUserNotFoundExceptions" value="false" />
<property name="passwordEncoder" ref="encoder" />
</bean>
Hope it will help somebody!
with 3.1.x this mapping doesnt work for auth.
Working way is:
<beans:bean id='bCryptPasswordEncoder' class='org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder'></beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder ref="bCryptPasswordEncoder"/>
</authentication-provider>
</authentication-manager>

Resources