How to Implement beanprocessor to examine the attribute of meta listener? - spring-kafka

My meta listener code
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#KafkaListener(containerFactory = "myListenerContainerFactory", autoStartup = "false")
public #interface mylistener1 {
#AliasFor(annotation = KafkaListener.class, attribute = "topics")
String[] topics();
String myattr() default "";
}
Consume method:
#Service
Class ConsumerService(){
#mylistener1(topics = "new.topic",myattr="new.myatr.topic")
public void consume(String message) {
LOG.info("consumer-> " + message);
}
}
I tried to get value from ApplicationContext, but it was not getting the listener.
#Autowired
ApplicationContext ctx;
Map<String, Object> allBeansWithNames = ctx.getBeansWithAnnotation(mylistener1.class);
allBeansWithNames - 0, and I am not getting class list which is having #mylistener1 annotation`your text`
I want to implement beanpostprocessor to check myattr at runtime and use it to send message`

getBeansWithAnnotation() will only find beans with classes that are so annotated (or #Bean factory methods). It doesn't look at the class methods.
Take a look at KafkaListenerAnnotationBeanPostProcessor for an example of a BPP that examines annotations and their attributes.

Related

How can i extend spring-kafka's '#KafkaListener' annotation to create my own annotation with limited attributes?

I've been using '#KafkaListener' at method level to create consumers. Now, I'm trying to create my own custom annotation by extending '#KafkaListener' and limit the no of attributes (for example, because of some reasons, I don't want to expose attributes like 'errorHandler' 'containerGroup' etc ). Now my question is, to implement this, is there any option to extend the existing '#KafkaListener' ? Please suggest.
Yes, it's quite easy.
#SpringBootApplication
public class So61684460Application {
public static void main(String[] args) {
SpringApplication.run(So61684460Application.class, args);
}
#MyListener(topics = "so61684460", id = "so61684460")
public void listen(String in) {
System.out.println(in);
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("so61684460").partitions(3).replicas(1).build();
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#KafkaListener(concurrency = "${my.concurrency}")
#interface MyListener {
#AliasFor(annotation = KafkaListener.class, attribute = "id")
String id();
#AliasFor(annotation = KafkaListener.class, attribute = "topics")
String[] topics() default "";
}
As you can see, as well as restricting the visibility of some attributes, you can make them required (id above), change the default value, or set hard-coded or parameterized values in the invisible attributes (concurrency above).
This is described in the documentation.

How do I test a Signal R hub that has LifetimeScope injected into it

How can I write unit tests to test my hub?
Here is my Hub Class:
public class MyHub : Hub
{
private readonly ILifetimeScope _scope;
private readonly IMyProvider _provider;
public MyHub(ILifetimeScope scope)
{
scope = _scope;
_provider = _scope.Resolve<IMyProvider>();
}
public void BroadcastMessage(int messageId)
{
var myMessage = _provider.GetFromDb(messageId);
Clients.All.broadcastMessage(myMessage);
}
}
I'm using moq and xunit, i've tried things like this:
//arrange
var messageId = 1;
var message = "test";
var providerMock = new Mock<IMyProvider>();
providerMock.Setup(x => x.GetFromDb(messageId).Returns(test);
var scopeMock = new Mock<ILifetimeScope>();
scopeMock.Setup(x => x.Resolve<IMyProvider>()).Returns(providerMock.Object);
var hub = new MyHub(scopeMock.Object);
//act
hub.BroadcastMessage(messageId);
//assert
providerMock.Verify(x => x.GetFromDb(messageId), Times.Once());
but this causes an error:
System.NotSupportedException : Unsupported expression: x => x.Resolve()
Extension methods (here: ResolutionExtensions.Resolve) may not be used in setup / verification expressions.
I found this answer: https://stackoverflow.com/a/49523868/3708225 that says I can do something like
using (var mock = AutoMock.GetLoose()){
var providerMock = new Mock<IMyPRovider>();
providerMock.Setup(x=>x.GetFromDb(messageId)).Returns(message);
mock.Provide(providerMock.Object);
var lifetime = mock.Create<ILifetimeScope>();
using (var scope = lifetimeScope.BeginLifetimeScope()){
var innerMockProvider = scope.Resolve<IMyProvider>();
//rest of test
}
}
but AutoMock.GetLoose().Provide() isn't defined
This is probably not the answer you are looking for. But a workaround would be not to mock lifetimescope but simply setup a autofac container to use in these tests.
Secondly do you need to inject the lifetimescope directly in your class? Maybe use a decorator pattern where you let the decorator create the lifetimescope and resolve your class and invoke it. Getting rid of the lifetimescope in your myhub class will make your life easier. Make it the job of some other class to control the lifetimescope. Else you will need to repeat this pattern in all your other classes as well. You should instead inject IMyProvider.
This is how I solved this:
If I change my hub to the following:
public class MyHub : Hub
{
private readonly <Func>IMyProvider _provider;
public MyHub(<Func>IMyProvider provider)
{
_provider = provider;
}
public void BroadcastMessage(int messageId)
{
var provider = _provider();
var myMessage = provider.GetFromDb(messageId);
Clients.All.broadcastMessage(myMessage);
}
}
Now i can mock Func<IMyProviider> to return what I need and ignore the lifetime scope

Spring OAuth2 Making `state` param at least 32 characters long

I am attempting to authorize against an external identity provider. Everything seems setup fine, but I keep getting a validation error with my identity provider because the state parameter automatically tacked onto my authorization request is not long enough:
For example:
&state=uYG5DC
The requirements of my IDP say that this state param must be at least 32-characters long. How can I programmatically increase the size of this auto-generated number?
Even if I could generate this number myself, it is not possible to override with other methods I have seen suggested. The following attempt fails because my manual setting of ?state=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz is superceded by the autogenerated param placed after it during the actual request:
#Bean
public OAuth2ProtectedResourceDetails loginGovOpenId() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails() {
#Override
public String getUserAuthorizationUri() {
return super.getUserAuthorizationUri() + "?state=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
}
};
details.setClientId(clientId);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("openid", "email"));
details.setPreEstablishedRedirectUri(redirectUri);
details.setUseCurrentUri(true);
return details;
}
The 6-character setting seems to be set here, is there a way to override this?
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/util/RandomValueStringGenerator.java
With the help of this post:
spring security StateKeyGenerator custom instance
I was able to come up with a working solution.
In my configuration class marked with these annotations:
#Configuration
#EnableOAuth2Client
I configured the following beans:
#Bean
public OAuth2ProtectedResourceDetails loginGovOpenId() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
AuthorizationCodeResourceDetails details = new
details.setClientId(clientId);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("openid", "email"));
details.setPreEstablishedRedirectUri(redirectUri);
details.setUseCurrentUri(true);
return details;
}
#Bean
public StateKeyGenerator stateKeyGenerator() {
return new CustomStateKeyGenerator();
}
#Bean
public AccessTokenProvider accessTokenProvider() {
AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
accessTokenProvider.setStateKeyGenerator(stateKeyGenerator());
return accessTokenProvider;
}
#Bean
public OAuth2RestTemplate loginGovOpenIdTemplate(final OAuth2ClientContext clientContext) {
final OAuth2RestTemplate template = new OAuth2RestTemplate(loginGovOpenId(), clientContext);
template.setAccessTokenProvider(accessTokenProvider());
return template;
}
Where my CustomStateKeyGenerator implementation class looks as follows:
public class CustomStateKeyGenerator implements StateKeyGenerator {
// login.gov requires state to be at least 32-characters long
private static int length = 32;
private RandomValueStringGenerator generator = new RandomValueStringGenerator(length);
#Override
public String generateKey(OAuth2ProtectedResourceDetails resource) {
return generator.generate();
}
}

in API, create multiple controller constructor with one parameter

[Route("api/[controller]")]
public class DigitalDocumentController : Controller
{
private IDigitalDocumentService digitalDocumentService;
private IDatabaseInitializer databaseInitializer;
public DigitalDocumentController(IDigitalDocumentService digitalDocumentService)
{
this.digitalDocumentService = digitalDocumentService;
}
public DigitalDocumentController(IDatabaseInitializer databaseInitializer)
{
this.databaseInitializer = databaseInitializer;
}
i want two controller constructor in my project to Mock in xUnit Testing, but there was an error in my swagger interface {
"error": "Multiple constructors accepting all given argument types have been found in type 'i2ana.Web.Controllers.DigitalDocumentController'. There should only be one applicable constructor."
}
can anybody help me how i can do it ?
…
what i am try to do , is to test Uniquness of the Name Field in my database
My testing code:
[Fact]
public void AddNotUniqueName_ReturnsNotFoundObjectResult()
{
var digitalDocument = new DigitalDocument
{
Image = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
CreatedOn = DateTime.Today,
Id = 6,
Location = "temp",
Name = "Flower",
Tages = new List<Tag> { new Tag { Id = 1, Value = "Tag 1" }, new Tag { Id = 1, Value = "Tag 2" } }
};
// Arrange
var mockRepo = new Mock<IDatabaseInitializer>();
mockRepo.Setup(repo => repo.SeedAsync()).Returns(Task.FromResult(AddUniqueDigitalDocument(digitalDocument)));
var controller = new DigitalDocumentController(mockRepo.Object);
// Act
var result = controller.Add(digitalDocument);
// Assert
var viewResult = Assert.IsType<NotFoundObjectResult>(result);
var model = Assert.IsAssignableFrom<int>(viewResult.Value);
Assert.NotEqual(6, model);
}
the "AddUniqueDigitalDocument" returns 6 only to test that the new digitaldocumet is not the same id of my initialize data.
When using dependency injection, you should only have one constructor where all dependencies can be satisfied. Otherwise, how is the DI container to know which constructor to utilize? That's your issue here. Using the Microsoft.Extensions.DependencyInjection package, and since this is a controller you're injecting into, there's only one reasonable way to solve this: don't register one or the other of the services, IDigitalDocumentService or IDatatabaseInitializer. If only one is registered, the service collection will simply use the constructor it has a registered service for.
It's possible with a more featured DI container, you might be able to configure something to allow it choose the proper constructor. How to do that would be entirely dependent on the DI container you end up going with, though, so not much more can be said on the subject at this point. Just realize that the default container (Microsoft.Extensions.DependencyInjection) is intentionally simplistic, so if you needs are more complex, you should sub in a full DI container.
UPDATE
You should be doing integration testing with the test host and an in-memory database. The basic approach is:
public MyTests()
{
_server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
_context = _server.Host.Services.GetRequiredService<MyContext>();
_client = _server.CreateClient();
}
In your app's Startup, create a virtual method:
public virtual void ConfigureDatabase(IServiceCollection services)
{
// normal database setup here, e.g.
services.AddDbContext<MyContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("Foo")));
}
Then, in ConfigureServices, replace your database setup with a call to this method.
Finally, in your test project, create a TestStartup class and override the ConfigureDatabase method:
public class TestStartup : Startup
{
public override void ConfigureDatabase(IServiceCollection services)
{
var databaseName = Guid.NewGuid().ToString();
services.AddDbContext<MyContext>(o =>
o.UseInMemoryDatabase(databaseName));
}
}
Now, in your tests you just make requests against the test client (which is just an HttpClient instance, so it works like any other HttpClient). You start by setting up your database with appropriate test data, and then ensure that the correct response is returned:
// Arrange
_context.Add(new DigitalDocument { Name = "Foo" });
await _context.SaveChanges();
// Act
// Submit a `DigitalDocument` with the same name via `_client`
// Assert
// Inspect the response body for some indication that it was considered invalid. Or you could simply assert that no new `DigitalDocument` was created by querying `_context` (or both)
This is admittedly a lot easier with an API, as with a web application, you're going to invariably need to do some HTML parsing. However, the docs and corresponding sample app help you with that.
Additionally, in actual practice, you'd want to use a test fixture to prevent having to bootstrap a test server for every test. Again, the docs have you covered there. One thing to note, though, is that once you switch to using a fixture, your database will then be persisted between tests. To segregate your test data, make sure that you call EnsureDeleted() on your context before each test. This can be easily done in the test class' constructor:
public class MyTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly MyContext _context;
public MyTests(WebApplicationFactory<Startup> factory)
{
factory = factory.WithWebHostBuilder(builder => builder.UseStartup<TestStartup>());
_client = factory.CreateClient();
_context = factory.Server.Host.Services.GetRequiredService<MyContext>();
_context.EnsureDeleted();
}
I don't even like this much bootstrapping code in my tests, though, so I usually inherit from a fixture class instead:
public class TestServerFixture : IClassFixture<WebApplicationFactory<Startup>>
{
protected readonly HttpClient _client;
protected readonly MyContext _context;
public TestServerFixture(WebApplicationFactory<Startup> factory)
{
factory = factory.WithWebHostBuilder(builder => builder.UseStartup<TestStartup>());
_client = factory.CreateClient();
_context = factory.Server.Host.Services.GetRequiredService<MyContext>();
_context.EnsureDeleted();
}
}
Then, for each test class:
public class MyTests : TestServerFixture
{
public MyTests(WebApplicationFactory<Startup> factory)
: base(factory)
{
}
This may seem like a lot, but most of it is one-time setup. Then, your tests will be much more accurate, more robust, and even easier in many ways.

How to get basePackages of #ComponentScan programatically at runtime?

The scanBasePackages of #SpringBootApplication configured as follow:
package com.xxx.boot.sample;
#SpringBootApplication(scanBasePackages = { "com.xxx.boot.sample", "com.xxx.boot.service" })
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
The requirement is we want to integration Apache Dubbo component annotation scan with Spring Boot by programming at runtime for zero properties configuration, not by annotation.
#Configuration
#ConditionalOnClass({ EnableDubboConfig.class, AbstractConfig.class })
#ConditionalOnProperty(prefix = "dubbo", name = "enabled", havingValue = "true", matchIfMissing = true)
public class DubboAutoConfiguration {
/// Dubbo配置
#Configuration
#EnableDubboConfig
#ConditionalOnProperty(prefix = "dubbo.config", name = "enabled", havingValue = "true", matchIfMissing = true)
#EnableConfigurationProperties(DubboProperties.class)
public static class DubboConfigConfiguration {
}
/// Dubbo注解扫描
#Configuration
#ConditionalOnClass({ Service.class, Reference.class })
#ConditionalOnProperty(prefix = "dubbo.annotation", name = "enabled", havingValue = "true", matchIfMissing = true)
public static class DubboAnnotationConfiguration {
#Bean(name = "serviceAnnotationBeanPostProcessor")
#ConditionalOnMissingBean
public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(BeanFactory beanFactory) {
// 获取 Spring Boot 主入口类所在的包路径
List<String> packagesToScan = AutoConfigurationPackages.get(beanFactory);
if (packagesToScan == null) {
packagesToScan = Collections.emptyList();
}
return new ServiceAnnotationBeanPostProcessor(packagesToScan);
}
#Bean(name = ReferenceAnnotationBeanPostProcessor.BEAN_NAME)
#ConditionalOnMissingBean
public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() {
return new ReferenceAnnotationBeanPostProcessor();
}
}
}
But AutoConfigurationPackages#get(BeanFactory) only return "com.xxx.boot.sample", not include "com.xxx.boot.service". I hope return all scanBasePackages value.
By debug, I found the #SpringBootApplication instance is a proxy class instance. I try to get Annotation use Class#getAnnotations, then get scanBasePackages field by reflection. But not success.
Question:
How to get scanBasePackages of #SpringBootApplication or basePackages of #ComponentScan programatically?
It isn't really advisable to try to reuse the scanBasePackages attributes for your own purposes. If you look at the source of #SpringBootApplication you'll see the following:
#AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
#AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
This is saying that these attributes are an alias for #ComponentScan. Since this annotation can be used on any #Configuration class it's actually legal to have many of them.
The #ComponentScan annotation triggers scanning by the ConfigurationClassParser. Look at the doProcessConfigurationClass method for all the gory details.
If you really want to find the annotation attributes yourself you can do the following:
applicationContext.getBeansWithAnnotation(ComponentScan.class).forEach((name, instance) -> {
Set<ComponentScan> scans = AnnotatedElementUtils.getMergedRepeatableAnnotations(instance.getClass(), ComponentScan.class);
for (ComponentScan scan : scans) {
System.out.println(Arrays.toString(scan.basePackageClasses()));
System.out.println(Arrays.toString(scan.basePackages()));
}
});
This will just get you those two values. You're still not considering any #Condition annotations or any include/exclude filters. You also won't deal with #ComponentScan() which means scan from the current package down.
What Spring Boot tends to do in these circumstances is define a new annotation for a specific purpose. For example, you can use #EntityScan to define where JPA entities are found. We then use AutoConfigurationPackages as the default value if you don't specify any override.

Resources