How do you override the Hystrix default configuration for OpenFeign? Most of the documentation out there is for SpringBoot + OpenFeign, which has its own Spring-specific configuration override system.
Ideally it would be possible to configure the Hystrix core size for the client and configure and timeouts on a per endpoint basis.
Hystrix OpenFeign has a setterFactory() method on the builder that allows you to pass in a SetterFactory lambda function that is executed when setting up each target endpoint:
final SetterFactory hystrixConfigurationFactory = (target, method) -> {
final String groupKey = target.name();
final String commandKey = method.getAnnotation(RequestLine.class).value();
// Configure default thread pool properties
final HystrixThreadPoolProperties.Setter hystrixThreadPoolProperties = HystrixThreadPoolProperties.Setter()
.withCoreSize(50)
.withMaximumSize(200)
.withAllowMaximumSizeToDivergeFromCoreSize(true);
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
.andThreadPoolPropertiesDefaults(hystrixThreadPoolProperties);;
};
final MyTargetClient myTargetClient = HystrixFeign.builder()
.setterFactory(hystrixConfigurationFactory)
.client(new OkHttpClient())
.encoder(new JacksonEncoder(objectMapper))
.decoder(new JacksonDecoder(objectMapper))
.target(new Target.HardCodedTarget<>(MyTargetClient.class, "customclientname", baseUrl))
The above example uses boilerplate from the OpenFeign documentation to properly name Hystrix keys based on the target endpoint function. It then goes further by also configuring the default thread pool property core size and maximum core size as a default for all of the target functions.
However, since this factory is called for each target endpoint, we can actually override the Hystrix configuration on a per endpoint basis. A use good case for this is Hystrix timeouts: sometimes there are endpoints that take longer than others and we need to account for that.
The easiest way would be to first create an annotation and place it on the target endpoints that need to be overridden:
/**
* Override Hystrix configuration for Feign targets.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface HystrixOverride {
int DEFAULT_EXECUTION_TIMEOUT = 2_000;
/**
* Execution timeout in milliseconds.
*/
int executionTimeout() default DEFAULT_EXECUTION_TIMEOUT;
}
interface MyTargetClient {
#HystrixOverride(executionTimeout = 10_000)
#RequestLine("GET /rest/{storeCode}/V1/products")
Products searchProducts(#Param("storeCode") String storeCode, #QueryMap Map<String, Object> queryMap);
#RequestLine("GET /rest/{storeCode}/V1/products/{sku}")
Product getProduct(#Param("storeCode") String storeCode, #Param("sku") String sku);
}
In the above example, the search API might take a little longer to load so we have an override for that.
Just putting the override annotation on the target endpoint function is not enough though. We need to go back to our factory and update it to use the data in the annotations:
final SetterFactory hystrixConfigurationFactory = (target, method) -> {
final String groupKey = target.name();
final String commandKey = method.getAnnotation(RequestLine.class).value();
// Configure per-function Hystrix configuration by referencing annotations
final HystrixCommandProperties.Setter hystrixCommandProperties = HystrixCommandProperties.Setter();
final HystrixOverride hystrixOverride = method.getAnnotation(HystrixOverride.class);
final int executionTimeout = (hystrixOverride == null)
? HystrixOverride.DEFAULT_EXECUTION_TIMEOUT
: hystrixOverride.executionTimeout();
hystrixCommandProperties.withExecutionTimeoutInMilliseconds(executionTimeout);
// Configure default thread pool properties
final HystrixThreadPoolProperties.Setter hystrixThreadPoolProperties = HystrixThreadPoolProperties.Setter()
.withCoreSize(50)
.withMaximumSize(200)
.withAllowMaximumSizeToDivergeFromCoreSize(true);
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
.andCommandPropertiesDefaults(hystrixCommandProperties)
.andThreadPoolPropertiesDefaults(hystrixThreadPoolProperties);;
};
The above checks that an override annotation exists and then uses the data in that annotation to configure the execution timeout for that target endpoint. If the override is not present, the default for the HystrixOverride endpoint will be used instead. The resulting hystrixCommandProperties variable is then plugged in to the overall HystrixCommand.Setter at the end.
Related
I have created a Hateoas enabled Rest service using spring-boot-starter-data-rest, works well.
I then created a client of that rest service in another spring boot module: this is a dependency that can be included in other projects that want to use the rest service. It uses a restTemplate under the hood.
It took a bit of mucking around with HttpMessageConverters and TypeConstrainedMappingJackson2HttpMessageConverter to get it to work but it does.
I tried using this dependency in my main application but it failed to populate the links in ResponseEntity< Resource< Myclass> >, leading to null pointer exceptions.
I couldn't track down the problem so I created a basic Spring Boot application 2.1.5.RELEASE and got the client working, then traced back the problem to this configuration in my main application which unfortunately is need for another problem:
spring:
main:
web-application-type: none
If this configuration is present it seems that hal+json isn't the first accepted media type
org.springframework.core.log.CompositeLog.debug(CompositeLog.java:147) : Accept=[application/json, application/hal+json, application/octet-stream, application/*+json]
When the configuration is removed I see
org.springframework.core.log.CompositeLog.debug(CompositeLog.java:147) : Accept=[application/hal+json, application/json, application/octet-stream, application/*+json]
and I can see this logged which fixes the issue I assume ( it isn't logged when the error happens)
- #ConditionalOnProperty (spring.hateoas.use-hal-as-default-json-media-type) matched (OnPropertyCondition)
I have tried adding this configuration to force the issue but it doesn't work
spring:
hateoas:
use-hal-as-default-json-media-type: true
This is my code in the rest client to configure the message converters:
#Configuration
public class MessageConverterConfiguration {
#Bean public TypeConstrainedMappingJackson2HttpMessageConverter myhalJacksonHttpMessageConverter(){
return new TypeConstrainedMappingJackson2HttpMessageConverter( ResourceSupport.class );
}
/**
* Add {#link TypeConstrainedMappingJackson2HttpMessageConverter} to the list of {#link HttpMessageConverter}s
* configured in the {#link RestTemplate} in first position ( this position is critical ).
* #param halJacksonHttpMessageConverter automagically configured by spring-boot-starter-hateoas
* #return List of {#link HttpMessageConverter}s
*/
#Bean( name = "hal-jackson" ) public List< HttpMessageConverter<?> > mymessageConverters( TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter ) {
final List<HttpMessageConverter<?>> all = new ArrayList<>( );
all.add( halJacksonHttpMessageConverter );
all.add( jacksonConverterWithOctetStreamSupport( ) );
all.addAll( new RestTemplate().getMessageConverters() );
return all;
}
/**
* This allows converting octet stream responses into {#link LastApplicationRun} ,
* when we create a last run by posting with {#link RestTemplate#postForObject(URI , Object, Class)}
* : without it we get a
* <pre>org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter
* found for response type [class com.sparknz.ced.spark.sampling.rest.tobesampled.client.domain.LastApplicationRun]
* and content type [application/octet-stream]</pre>.
* <p></p>
* I could find no better solution: it is not needed when we make a get call, don't understand why we get an octet stream response.
* It may only now be useful for tests.
*/
private MappingJackson2HttpMessageConverter jacksonConverterWithOctetStreamSupport( ) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(
asList(new MediaType[]{
MediaType.valueOf( "application/hal+json" ) ,
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM }));
return converter;
}
}
What is 'web-application-type: none' doing and how can I get HypermediaHttpMessageConverterConfiguration to run?
I found that adding this to my configuration class did the trick:
#Import( RepositoryRestMvcConfiguration.class )
RepositoryRestMvcConfiguration seems to be responsible for making hal+json the highest priority by adding RepositoryRestMvcConfiguration.ResourceSupportHttpMessageConverter at position 0 in the list of HttpMessageConverters.
I implemented a mock ExecutorService to return the result instantly without the need to create threads:
public static ExecutorService createMock() throws Exception {
ExecutorService executorServiceMock = EasyMock.createMock(ExecutorService.class);
Future future = EasyMock.createMock(Future.class);
Capture<Callable<?>> callableCapture = new Capture<>();
EasyMock.expect(executorServiceMock.submit(EasyMock.<Callable<?>>capture(callableCapture))).andReturn(future).anyTimes();
EasyMock.expect(future.get()).andAnswer(() -> callableCapture.getValue().call()).anyTimes();
executorServiceMock.shutdown();
EasyMock.expectLastCall().anyTimes();
EasyMock.replay(future, executorServiceMock);
return executorServiceMock;
}
The problem is that it always returns the same [mocked] Future object. I need to return the new instance of Future mock based on the callable object passed to executorServiceMock.submit()
I tried to use PowerMock.expectNew(Future.class) but it complained "No constructor found in class 'java.util.concurrent.Future' with parameter types: [ ]"
First of all "Don't mock type you don't own!". By link you may find several reasons why you shouldn't do it.
But, if you really want to do it, then replace return mock via answer witch will create a new nock each time:
EasyMock.expect(executorServiceMock.submit(EasyMock.<Callable<?>>capture(callableCapture))).andAnswer(() -> {
Future future = EasyMock.createMock(Future.class);
EasyMock.expect(future.get()).andAnswer(() -> callableCapture.getValue().call()).anyTimes();
EasyMock.replay(future);
return future;
}).anyTimes();
By the way, you cannot expected PowerMock.expectNew(Future.class) because Futureis interface and cannot be created.
The capture will always return the last captured object. In your case, you seem to want to synchronize the submission and the get.
So, I think you should do the following:
ExecutorService executorServiceMock = createMock(ExecutorService.class);
expect(executorServiceMock.submit(EasyMock.<Callable<?>>anyObject()))
.andAnswer(() -> {
Future future = createMock(Future.class);
Object value = ((Callable<?>) getCurrentArguments()[0]).call();
expect(future.get()).andReturn(value);
replay(future);
return future;
})
.anyTimes();
executorServiceMock.shutdown();
expectLastCall().anyTimes();
replay(executorServiceMock);
return executorServiceMock;
Below code fixed my issue.
#Mock
private ThreadPoolExecutor executorPool;
Future<String> result = CompletableFuture.completedFuture("S3PATH");
when(executorPool.submit(Mockito.<Callable<String>> anyObject())).thenReturn(result);
I have a shared API and services are annotated
#Produces({"application/json","application/x-jackson-smile"})
#Consumes({"application/json","application/x-jackson-smile"})
public class AServiceClass {
So default is JSON - this will be preferred when using browser AJAX calls.
However I have a RestEasy client which I create using
ProxyFactory.create(AServiceClass.class, url)
And I want this client to use SMILE for both inbound and aoutbound communication. Of course it picks first item from #Consumes and tries marshalling to JSON.
I'm using RestEasy 2.3.5
How to force the client to use SMILE marshalling?
How to force the client to accept SMILE instead of JSON?
Actually it turns out that you ( I mean I :-) ) can't do this.
Checking MediaTypeHelper.getConsumes() shows that always first annotation value is picked to determine marshalling media type.
return MediaType.valueOf(consume.value()[0]);
The same happens when it comes to figuring out accept header. The code uses MediaTypeHelper.getProduces()
It can be done by specifying the value for the header Accept
Response response = client.target(host + "/yourpath").request()
.header(HttpHeaders.ACCEPT, "application/x-jackson-smile")
.get();
You can also achieve this with a ClientRequestFilter, which is more usful if you are using proxies of your JAX-RS annotated classes. For example
public class AcceptedHeaderFilter implements ClientRequestFilter
{
private final MediaType acceptedType;
public AcceptedHeaderFilter(final MediaType acceptedType)
{
super();
this.acceptedType = acceptedType;
}
#Override
public void filter(final ClientRequestContext requestContext) throws IOException
{
requestContext.getHeaders().get(HttpHeaders.ACCEPT).clear();
requestContext.getHeaders().get(HttpHeaders.ACCEPT).add(acceptedType.toString());
}
}
If you are using Resteasy, you can register on your ResteasyWebTarget
MediaType contentType = MediaType.APPLICATION_XML_TYPE;
final ResteasyWebTarget target = buildTarget();
target.getResteasyClient().register(new AcceptedHeaderFilter(contentType));
CXF or Jersey will let you register the filter, but will require you to do it in a slightly different way.
I am using Java EE 6 and need to load configuration from a ".properties" file. Is there a recommended way (best practice) to load the values from the configuration file using dependency injection? I found annotations for this in Spring, but I have not found a "standard" annotation for Java EE.
This guy have developed a solution from scratch:
http://weblogs.java.net/blog/jjviana/archive/2010/05/18/applicaction-configuration-java-ee-6-using-cdi-simple-example
"I couldn't find a simple example of how to configure your application
with CDI by reading configuration attributes from a file..."
But I wonder if there is a more standard way instead of creating a configuration factory...
Configuration annotation
package com.ubiteck.cdi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectedConfiguration {
/**
* Bundle key
* #return a valid bundle key or ""
*/
#Nonbinding String key() default "";
/**
* Is it a mandatory property
* #return true if mandator
*/
#Nonbinding boolean mandatory() default false;
/**
* Default value if not provided
* #return default value or ""
*/
#Nonbinding String defaultValue() default "";
}
The configuration factory could look like :
import java.text.MessageFormat;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
public class ConfigurationInjectionManager {
static final String INVALID_KEY="Invalid key '{0}'";
static final String MANDATORY_PARAM_MISSING = "No definition found for a mandatory configuration parameter : '{0}'";
private final String BUNDLE_FILE_NAME = "configuration";
private final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_FILE_NAME);
#Produces
#InjectedConfiguration
public String injectConfiguration(InjectionPoint ip) throws IllegalStateException {
InjectedConfiguration param = ip.getAnnotated().getAnnotation(InjectedConfiguration.class);
if (param.key() == null || param.key().length() == 0) {
return param.defaultValue();
}
String value;
try {
value = bundle.getString(param.key());
if (value == null || value.trim().length() == 0) {
if (param.mandatory())
throw new IllegalStateException(MessageFormat.format(MANDATORY_PARAM_MISSING, new Object[]{param.key()}));
else
return param.defaultValue();
}
return value;
} catch (MissingResourceException e) {
if (param.mandatory()) throw new IllegalStateException(MessageFormat.format(MANDATORY_PARAM_MISSING, new Object[]{param.key()}));
return MessageFormat.format(INVALID_KEY, new Object[]{param.key()});
}
}
Tutorial with explanation and Arquillian test
Even though it does not exactly cover your question, this part of the Weld documentation might be of interest for you.
Having mentioned this - no, there is no standard way to inject arbitrary resources / resource files. I guess it's simply beyond the scope of a spec to standardise such highly custom-dependent requirement (Spring is no specification, they can simply implement whatever they like). However, what CDI provides is a strong (aka typesafe) mechanism to inject configuration-holding beans on one side, and a flexible producer mechanism to read and create such beans on the other side. Definitely this is the recommended way you were asking about.
The approach you are linking to is certainly a pretty good one - even though it might me too much for your needs, depending on the kind of properties you are planning to inject.
A very CDI-ish way of continuing would be to develop a CDI extension (that would nicely encapsulate all required classes) and deploy it independently with your projects. Of course you can also contribute to the CDI-extension catalog or even Apache Deltaspike.
See #ConfigProperty of Apache DeltaSpike
The only "standard" way of doing this would be to use a qualifier with a nonbinding annotation member, and make sure all of your injections are dependent scoped. Then in your producer you can get a hold of the InjectionPoint and get the key off the qualifier in the injection point. You'd want something like this:
#Qualifier
public #interface Property {
#Nonbinding String value default "";
}
...
#Inject #Property("myKey") String myKey;
...
#Produces #Property public String getPropertyByKey(InjectionPoint ip) {
Set<Annotation> qualifiers = ip.getQualifiers
// Loop through qualifers looking for Property.class save that off
return ResourceBundle.getBundle(...).getString(property.key);
}
There are obviously some enhancements you can do to that code, but it should be enough to get you started down the right track.
I am using MVC 3, I have a range of Controllers depending on various repositories, 1 of my repository relies on the http context session.
I have created interfaces for each of my repositories in order to use the Windsor-Castle IoC.
How can I pass the current session object down to the repository that needs it?
I used to be able to do this and the "Resolve" would take care passing the session to the repository that needed it, somehow I cannot do this in the latest version (2.5.3 Feb 2011):
Protected Overrides Function GetControllerInstance(ByVal requestContext As System.Web.Routing.RequestContext, _
ByVal controllerType As System.Type) As System.Web.Mvc.IController
Dim match As IController
' 1 or more components may need the session,
' adding it as a (possible) dependency
Dim deps As New Hashtable
deps.Add("session", HttpContext.Current.Session)
match = container.Resolve(controllerType, deps)
Return match
End Function
Thanks, Vincent
Look closely at your design. When you look at it functionaly, your repository doesn't really depend on the the session at all, but at some data you store in the session. Create an abstraction over the things you want to extract from the session and let the repository depend on such abstraction. For instance:
public interface IUserProvider
{
int GetCurrentUserId();
}
public class SomeRepository : ISomeRepository
{
private readonly IUserProvider userProvider;
public SomeRepository(IUserProvider userProvider)
{
this.userProvider = userProvider;
}
}
Now you can create the following implementation of that abstraction:
private class HttpSessionUserProvider : IUserProvider
{
public int GetCurrentUserId()
{
return (int)HttpContext.Current.Session["UserId"];
}
}
You can register this concrete type in your IoC configuration.
This is much better, because you don't want to let your repository depend directly on the HTTP session. This makes testing harder and creates a dependency between your repository and a specific presentation technology.
The controller factory's sole responsibility is to create controllers. Not handling sessions or any other dependencies. It's best to just register the session as a separate component and let Windsor autowire it. Remove the 'deps' Hashtable from there and register:
container.Register(Component.For<HttpSessionStateBase>()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(() => new HttpSessionStateWrapper(HttpContext.Current.Session)));
Then inject HttpSessionStateBase in your controller.
BTW: controllers already have access to the session, there is no need to do this if you're just injecting the session to controllers.