load Spring aggregator parametrs dynamically for tests - java

I'm using the aggregator of spring integration in my app.
I'm using cucumber to test the flow, and I want to make sure that the aggregator release
strategy is executed by the right parameters. I have 2 params for that strategy:
timeout, and size.
I would like to know if there is a way to load those parameters dynamically during the steps?
So far I'm lost about how to make it work.
Thanks,
Thank you Artem,
I used the directFieldAccessor and I'm trying to get the release-strategy-expression and group-timeout attributes from the aggregator, but the problem is that I can't access those fields:
public void setAggregatorConfiguration(String aggregatorName, int aggregationThreshold, long aggregationTimeout) {
EventDrivenConsumer aggregator = getAggregator(aggregatorName);
DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(aggregator);
directFieldAccessor.setPropertyValue("group-timeout", aggregationTimeout);
}

I have 2 params for that strategy: timeout, and size
Well, I guess you use TimeoutCountSequenceSizeReleaseStrategy. As we see those params are final in that class and they really can't be changed at runtime just with setters.
There is only one way to change such values at runtime - using DirectFieldAccessor.
However if you share more info about your use-case and config and unit test as well, we might find some other way to help you.
I've edited your post to add your code. No, you have to use the real property name, so it should be groupTimeoutExpression, not group-timeout. From other side there is a public setter on the matter: AbstractCorrelatingMessageHandler.setGroupTimeoutExpression(Expression groupTimeoutExpression).
From other side you go a bit wrong way: any MessageHandler component (<aggregator>, <service-activator>) provides several component. Right, the root of them is a AbstractEndpoint (the parent of EventDrivenConsumer). But all of your desired properties are to the AggregatingMessageHandler, which can get from BeanFactory using its alias - aggregatorName + ".handler".
release-strategy-expression is property of ExpressionEvaluatingReleaseStrategy, which you can change using DirectFieldAccessor on the AggregatingMessageHandler:
AggregatingMessageHandler handler = beanFactory.getBean(aggregatorName + ".handler", AggregatingMessageHandler.class);
DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(handler);
ReleaseStrategy releaseStrategy = (ReleaseStrategy) directFieldAccessor.getPropertyValue("releaseStrategy");
DirectFieldAccessor dfa = new DirectFieldAccessor(releaseStrategy);
dfa.setPropertyValue("expression", ...);
Anyway, it isn't clear, why you need such a perversion...

Related

How do I replace the values of a YAML file with definitions in the same file? [duplicate]

We have a spring boot application with configuration being driven from application.yml file. In this configuration file we use the feature of defining a property by referring to another property inside the same application.yml file:
my-games-app:
base-property: foo
games:
- game-one:
game-name: ${my-games-app.base-property}one
game-location: ${my-games-app.base-property}/one
- game-two:
game-name: ${my-games-app.base-property}two
game-location: ${my-games-app.base-property}/two
And we have a #ConfigurationProperties bean loading games configuration:
#Configuration
#ConfigurationProperties(prefix = "my-games-app.games")
public class GamesConfig {
private Map<String, Game> games;
...
}
Useless to say the above is just an example, in reality it is a very complex setup with GamesConfig bean being used as a constructor argument for many other beans inside our application:
#Component
public class GamesRunner {
private final GamesConfig gamesConfig;
...
}
Everything works as expected. The problem we have is related to testing the beans where GamesConfig is injected; in the above example GamesRunner. At the moment we use #SpringBootTest to get hold of the beans we want to test. This again, works OK but the main inconvenient is that the whole application needs to be started in order to access the GamesConfig bean. This means setting up a lot of infrastructure such as a Database a JMS message broker and a Kafka broker. This takes time and makes our CI builds longer to run which started to become a bit of an inconvenient. Because the beans we want to test don't need any other setup than having the GamesConfig constructor argument provided we would prefer to have unit tests in place rather than integration tests as they are much faster to run.
In other words, we want to be able to recreate GamesConfig by hand by parsing our application.yml with a test helper method. To do this we use snakeyaml library:
public final class TestHelper {
public static GamesConfig getGamesConfig() {
var yaml = new Yaml();
var applicationYaml = (Map<String, Object>) yaml.load(readResourceAsString("application.yml");
return createGamesConfig(applicationYaml.get("games");
}
private static GamesConfig createGamesConfig(Object config) {
// The config Object passed here is a `Map<String, Map<String, String>>`
// as defeined in our `games` entry in our `application.yml`.
// The issue is that game name and game locations are loaded exactly like
// configured without property place holders being resolved
return gamesConfig;
}
}
We resolved the issue by manually parsing the property placeholders and looking up their values in the application.yml file. Even if our own property placeholder implementation is quite generic, my feeling is that this extra work is not needed as it should be a basic expectation the library would have some specific set up to do this out of the box. Being very new to snakeyaml I hope someone else hit the same problem and knows how to do it.
We use snakeyaml because it just happened to be in the class path as a transitive dependency, we are open to any suggestions that would achieve the same thing.
Thank you in advance.
To my knowledge, SnakeYAML only supports substitution of environment variables, which is why what you want is not possible as far as I know. What you can do instead, of course, is simply use Spring's classes without setting up a full ApplicationContext.
For example, assuming your game config from above, you could use:
final var loader = new YamlPropertySourceLoader();
final var sources = loader.load(
"games-config.yml",
new ClassPathResource("games-config.yml")
);
final var mutablePropertySources = new MutablePropertySources();
sources.forEach(mutablePropertySources::addFirst);
final var resolver = new PropertySourcesPropertyResolver(mutablePropertySources);
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
System.out.println(resolver.getProperty("my-games-app.games[0].game-one.game-name"));
System.out.println(resolver.getProperty("my-games-app.games[0].game-one.game-location"));
System.out.println(resolver.getProperty("my-games-app.games[1].game-two.game-name"));
System.out.println(resolver.getProperty("my-games-app.games[1].game-two.game-location"));
which outputs:
fooone
foo/one
footwo
foo/two
If you are actually interested in how Spring does it, a good starting point is the source code of the PropertySourcesPlaceholderConfigurer class.

Use ArchUnit As Adapter to Run Architecture Test Based on External AnalyzeClasses

I am trying to do one example with ArchUnit where passing the AnalyzeClasses can be dynamic based on for which Adapter Application the test need run.
For Example:
#AnalyzeClasses(packages = "${archtest.scan.package}", importOptions = { ImportOption.DoNotIncludeTests.class, ImportOption.DoNotIncludeJars.class })
public class ArchitectureTests {
}
And from application.properties file it should allow to pass the packages to analyze dynamically, so any application using this Application as Jar library can provide the scan classes in its properties file. As below.
archtest.scan.package=com.example.pkgname
I am not sure what is the right way to pick up the dynamic value from property and pass that into #AnalyzeClasses Annotation. I am looking for some help or any example in this regard.
I don't think that ArchUnit's JUnit 4 & 5 support – in the current version 0.23.1 – allows for dynamic packages configured via an application.properties.
But instead of using #AnalyzeClasses, you can always just invoke new ClassFileImporter().import… and pass any dynamic runtime values you like.
(Note that ArchUnit's JUnit support also introduces a clever cache to reuse imported JavaClasses by multiple #ArchTests, but storing JavaClasses in a static field may be also good enough.)
This actually should be possible using a custom LocationProvider within #AnalyzeClasses. E.g.
#AnalyzeClasses(locations = ApplicationPropertiesLocationProvider.class)
public class ExampleTest {
// ...
}
class ApplicationPropertiesLocationProvider implements LocationProvider {
#Override
public Set<Location> get(Class<?> testClass) {
String packageToScan = readFromApplicationProperties();
return Locations.ofPackage(packageToScan);
}
}
But be aware of caching limitations! The caching mechanism assumes that your LocationProvider is "idempotent", i.e. it always returns the same locations. The caching mechanism will only take the type of the LocationProvider into consideration as cache key. This should not be a problem for a static application.properties as source though.

Swagger/Openapi-Annotations: How to produce allOf with $ref?

I'm generating Rest endpoints including adding Openapi/Swagger annotations to the generated code.
While it works quite well with basic types, I have some problems with custom classes.
Right now I have a lot of duplicate schema entries for the custom classes (using #Schema(implementation = MyClass.class)) but at least the needed information is there. However I'd like to find a way to remove the duplicate schema entries while retaining the additional information.
On a github-issue discussing the $ref and lack of sibling properties I found an example how you would write it manually in yaml in order to get the result I'm looking for, however I can't figure out how to set the annotations to produce it.
This is how I think the annotation should look like if I follow the example (just to be on the safe side it is added to both the getter and the setter):
import io.swagger.v3.oas.annotations.media.Schema;
...
public class SepaPaymentRequest {
...
#Schema(name = "w307BetrBeg", description = "BETRAG BEGUENSTIGTER ", allOf = { com.diesoftware.services.utils.Betrag.class }, required = true)
public void setW307BetrBeg(final Betrag w307BetrBeg) {
this.w307BetrBeg = w307BetrBeg;
}
...
}
However what I get when I fetch the openapi.yaml (snippet):
w307BetrBeg:
$ref: '#/components/schemas/Betrag'
What I'd like to have:
w307BetrBeg:
title: 'Betrag'
description: 'BETRAG BEGUENSTIGTER'
allOf:
- $ref: '#/components/schemas/Betrag'
Any hints are more than welcome.
I haven't found a way to do it using annotations, i.e. by annotating the class.
I think it's possible to do, by:
Creating a model
Injecting the model using a ModelConverter
When I say "a model" I mean an instance of io.swagger.v3.oas.models.media.Schema.
In particular I think you'd want to create and inject a io.swagger.v3.oas.models.media.ComposedSchema instance, which supports allOf.
Doing this (i.e. creating model instances) isn't very different from hand-writing the YAML.
Another possibility -- which I haven't tried -- might be to write a slightly different ModelConverter, which you install into the chain of converters. Then, intercept calls to resolve which return a SchemaObject whose name is Betrag, and (sometimes?) replace that with a ComposedSchema instance which uses allOf.

Is the model or a service responsible for defining the default TTL to a new instance of the model?

Here is the thing, I want to add ttl in my Dynamo tables, and it has a default Duration, but I want it to be configurable by properties, for each Spring Profile to have it's own configuration.
public Long getTtl() {
return ttl;
}
public void setTtl(Long ttl) {
this.ttl = ttl;
}
And to populate it correctly I will need to do something like this.
entity.setTtl(Instant.now().plus(Duration.from(defaultTTL)).getEpochSecond());
this defaultTTL I would load from an #Value or something.
But my question is where to put it. My first instinct was to set in the default constructor, but I don't like to load Spring values in it.
Following the "a code that changes together, belong together."
Am I wrong?
I defined the DEFAULT_TTL in a constant in a service for the CRUD operations and set the TTL in the Model's Constructor.
It looks good.

Unit testing stripes validation annotations directly

Stripes allows you to validate your form input values using the #Validate annotation on your member variables. Does anyone have any experience testing these annotations directly. I could do this by testing the validation errors that come back from the ActionBean, but this seems a little long winded and I would like a more direct method of testing if an input value is valid.
I'm not that familiar with the innards of the Framework yet, and I was hoping someone could give me some direction on where to start. TIA.
One method I've used is Stripes' built in MockRoundtrip. It is useful for simulating a complete test of an action bean event outside the container.
Example from the documentation:
MockServletContext context = ...;
MockRoundtrip trip = new MockRoundtrip(context, CalculatorActionBean.class);
trip.setParameter("numberOne", "2");
trip.setParameter("numberTwo", "2");
trip.execute();
CalculatorActionBean bean = trip.getActionBean(CalculatorActionBean.class);
Assert.assertEquals(bean.getResult(), 4, "two plus two should equal four");
Assert.assertEquals(trip.getDestination(), ""/quickstart/index.jsp");
Additionally, you could use trip.getValidationErrors() and assert that your error is in there.

Categories