Dynamic Queues on RabbitListener Annotation - java

I'd like to use queue names using a specific pattern, like project.{queue-name}.queue. And to keep this pattern solid, I wrote a helper class to generate this name from a simple identifier. So, foo would generate a queue called project.foo.queue. Simple.
But, the annotation RabbitListener demands a constant string and gives me an error using my helper class. How can I achieve this (or maybe another approach) using RabbitListener annotation?
#Component
public class FooListener {
// it doesn't work
#RabbitListener(queues = QueueName.for("foo"))
// it works
#RabbitListener(queues = "project.foo.queue")
void receive(final FooMessage message) {
// ...
}
}

To create and listen to a queue name constructed from a dynamic UUID, you could use random.uuid.
The problem is that this must be captured to a Java variable in only one place because a new random value would be generated each time the property is referenced.
The solution is to use Spring Expression Language (SpEL) to call a function that provides the configured value, something like:
#RabbitListener(queues = "#{configureAMQP.getControlQueueName()}")
void receive(final FooMessage message) {
// ...
}
Create the queue with something like this:
#Configuration
public class ConfigureAMQP {
#Value("${controlQueuePrefix}-${random.uuid}")
private String controlQueueName;
public String getControlQueueName() {
return controlQueueName;
}
#Bean
public Queue controlQueue() {
System.out.println("controlQueue(): controlQueueName=" + controlQueueName);
return new Queue(controlQueueName, true, true, true);
}
}
Notice that the necessary bean used in the SpEL was created implicitly based on the #Configuration class (with a slight alteration of the spelling ConfigureAMQP -> configureAMQP).

Declare a magic bean, in this case implicitly named queueName:
#Component
public class QueueName {
public String buildFor(String name) {
return "project."+name+".queue";
}
}
Access this using a "constant string" that will be evaluated at runtime:
#RabbitListener(queues = "#{queueName.buildFor(\"foo\")}")

If {queue-name} would came from yml file - it should work:
#RabbitListener(queues = "${queue-name}")
public void receiveMessage(FooMessage message) {
}
Spring will inject value from application.yml.

Related

Is it safe to use String as a return type of a bean in spring?

#Configuration
public class Product {
#Bean("xyz")
public String getMethod() {
return "abc";
}
}
#Component
public class Test {
String b;
Test(String xyz) {
this.b = xyz;
}
}
Is this any harm with this approach? I am trying to make change in the existing code where I am replacing the #Value with the getter as the method parameter. As I don't want to change the structure of the existing code I am trying to inject the method as bean as a replacement to #Value.
I suggest you to keep the #Value annotation instead of the whole #Bean configurations.
Why?
What if the getMethod()'s returned value needs to be changed very often? Everytime when you're changing something in the Product class, during build time it needs to be recompiled. What happens if the project is getting bigger and you're using this approach? It leads to longer build time and the more important thing is that this solution is not intuitive and it's hard to keep it clean. Don't think about complex solutions only to make the code look fancy. When you need to inject String values, the easiest approach is to create properties files (which won't get recompiled) and use the #Value annotation.
Now, if you want to add new methods without changing the structure of the existing code there are some patterns which you can apply like decorator pattern.
The main idea is simple: you're creating a decorator class which has an object of the type you need.
The easiest example (which you'll find everywhere on the internet) is the classic Shape example:
public interface Shape {
String someMethod();
}
#Component
public class CustomShape implements Shape { //implement the method here }
And here is the decorator:
public interface ShapeDecorator {
String someMethodExtended();
void someExtraMethod();
}
#Component
public class CustomShapeDecorator implements ShapeDecorator{
#Autowired
// #Qualifier - optional (only if you have more Shape implementations)
private Shape shape;
// now you can either:
// 1. provide new methods
#Override
public void someExtraMethod(){
System.out.println("Hello world!");
}
// 2. or you can EXTEND the Shape's "someMethod()" implementation
#Override
public String someMethodExtended(){
String oldString = this.shape.someMethod();
return oldString + " EXTENDED";
}
}

Spring boot: push message to specific topic for each request

I am using pub sub integration with spring boot, for which my configuration class look like this:
#Configuration
public class PubSubConfiguration {
#Value("${spring.pubsub.topic.name}")
private String topicName;
#Bean
#ServiceActivator(inputChannel = "MyOutputChannel")
public PubSubMessageHandler messageSender(PubSubTemplate pubsubTemplate) {
return new PubSubMessageHandler(pubsubTemplate, topicName);
}
#MessagingGateway(defaultRequestChannel = "MyOutputChannel")
public interface PubsubOutboundGateway {
void sendToPubsub(String attribute);
}
}
So now, I was calling only sendToPubSub method which add payload into topic from my app, like this:
#Autowired
private PubSubConfiguration.PubsubOutboundGateway outboundGateway;
// used line in my code wherever is needed.
outboundGateway.sendToPubsub(jsonInString);
The above code is just meant for one topic which i loaded from application property file.
But now I wanted to make my topic name is dynamically added into messageSender, how to do that.
To override the default topic you can use the GcpPubSubHeaders.TOPIC header.
final Message<?> message = MessageBuilder
.withPayload(msg.getPayload())
.setHeader(GcpPubSubHeaders.TOPIC, "newTopic").build();
and modify your sendToPubsub(Message<byte[]> message) to use message as input.
Refer for more information
Consider creating a BeanFactory to generate a PubSubMessageHandler Bean given a topic name. PubSubMessageHandler also has a setTopic() method, which may be of use.

Can we have a factory class as spring bean and have a factory method returning multiple spring beans based on the condition?

I want to return multiple spring beans based on the condition in the factory class.
Is this a good practice?
Any better ways to write the following piece of code?.
Any other design patterns suitable here?
Below is the code snippet:
package com.test;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
#Component
public class InstanceFactory {
#Resource(name = "instance1")
private Instance instance1;
#Resource(name = "instance2")
private Instance instance2;
public Instance getService(Condition condition) {
if (condition.one() && condition.two()) {
return instance2;
} else {
return instance1;
}
}
}
It depends on what you want to achieve. Factory Pattern is meant to create objects but what you are returning are objects already create somewhere else (Spring in this case). If you want to create beans that will be managed by Spring there are several ways:
#Conditional(YourConditionImplementation.class): This annotation added on a method of a #Configuration annotated class will allow you to create a bean when the given condition is fullfilled. Example here: https://javapapers.com/spring/spring-conditional-annotation/
You can uses as well BeanFactory to inject the definition of your bean (DefinitionBean) into the container. Example here: https://www.logicbig.com/tutorials/spring-framework/spring-core/bean-definition.html
Now, if you want an object that determine what object of type Instance fits better for some need then your approach is ok, but it is not technically a factory :)
When designing something like that I would face that solution considering two design patterns:
Strategy pattern: In order to replace repetitive if else every time you need to evaluate more instances.
Decorator pattern: Trying to make every condition as configurable as possible. They can be composed (decorated) for one or more predicates.
Considering these two pattens you might achieve something like this:
First, define which conditions will identify a given instance:
public enum InstanceType {
INSTANCE_TYPE_1(Condition::isOne, Condition::isTwo),
INSTANCE_TYPE_2(Condition::isOne, Condition::isThree),
...;
private List<Predicate<Condition>> evaluators;
#SafeVarargs
InstanceType(final Predicate<Condition>... evaluators) {
this.evaluators = Arrays.asList(evaluators);
}
public boolean evaluate(final Condition condition) {
return evaluators.stream().allMatch(it -> it.test(condition));
}
}
Then, you should link every instance implementation to an specific instance type:
#Component
public class InstanceOne implements Instance {
#Override
public InstanceType getType() {
return InstanceType.INSTANCE_TYPE_1;
}
}
Finally, a class to config where defining the relation between types and instances as EnumMap
#Configuration
public class InstanceFactoryConfig {
#Autowired
private List<Instance> instances;
#Bean
public EnumMap<InstanceType, Instance> instancesMap() {
EnumMap<InstanceType, Instance> instanceEnumMap = new EnumMap<>(InstanceType.class);
instances.forEach(i -> instanceEnumMap.put(i.getType(), i));
return instanceEnumMap;
}
}
Thus, you InstanceFactory can be replaced to something like this:
public class InstanceFactory {
#Autowire
private final EnumMap<InstanceType, Instance> instancesMap;
public void getInstance(Condition condition) {
instancesMap.get(getInstanceType(condition)).doSomething();
}
private InstanceType getInstanceType(Condition condition) {
return Arrays.stream(InstancesType.values())
.filter(evaluator -> evaluator.evaluate(condition))
.findFirst().orElseThrow(() -> new RuntimeException("Instance type not found"));
}
}
As you can see, you InstanceFactory is less prone to be modified. This means, every time you need you add a new instance implementation you only need to modify the InstanceType enum. Hope this is helps.
You can use spring existing FactoryBean interface and implement your own logic
It’s one of the best approaches to create beans in spring framework
Here is the link with example :
https://www.baeldung.com/spring-factorybean
See:
Spring Profile
The active profile is set by properties and based on the value you assign to the profile, Spring will load different beans for the same interface.
So it might be exactly what you need.

Dynamic dependency injection for multiple implementations of the same interface with Spring MVC

I am working on a REST API where I have an interface that defines a list of methods which are implemented by 4 different classes, with the possibility of adding many more in the future.
When I receive an HTTP request from the client there is some information included in the URL which will determine which implementation needs to be used.
Within my controller, I would like to have the end-point method contain a switch statement that checks the URL path variable and then uses the appropriate implementation.
I know that I can define and inject the concrete implementations into the controller and then insert which one I would like to use in each particular case in the switch statement, but this doesn't seem very elegant or scalable for 2 reasons:
I now have to instantiate all of the services, even though I only need to use one.
The code seems like it could be much leaner since I am literally calling the same method that is defined in the interface with the same parameters and while in the example it is not really an issue, but in the case that the list of implementations grows ... so does the number of cases and redundant code.
Is there a better solution to solve this type of situation? I am using SpringBoot 2 and JDK 10, ideally, I'd like to implement the most modern solution.
My Current Approach
#RequestMapping(Requests.MY_BASE_API_URL)
public class MyController {
//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;
//== CONSTRUCTORS ==
#Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3){
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;
}
//== REQUEST MAPPINGS ==
#GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(#PathVariable String source,
#RequestParam String start,
#RequestParam String end){
source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source)){
switch(source){
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;
}
}else{
//An invalid source path variable was recieved
}
//Return something after additional processing
return null;
}
}
In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.
Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :
interface MyInterface() {
(...)
String name()
}
This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).
When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.
A simplistic implementation in Spring Boot could look like this:
#SpringBootApplication
public class DynamicDependencyInjectionForMultipleImplementationsApplication {
public static void main(String[] args) {
SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);
}
interface MyInterface {
Object getStuff();
}
class Implementation1 implements MyInterface {
#Override public Object getStuff() {
return "foo";
}
}
class Implementation2 implements MyInterface {
#Override public Object getStuff() {
return "bar";
}
}
#Configuration
class Config {
#Bean("getFoo")
Implementation1 implementation1() {
return new Implementation1();
}
#Bean("getBar")
Implementation2 implementation2() {
return new Implementation2();
}
}
#RestController
class Controller {
private final Map<String, MyInterface> implementations;
Controller(Map<String, MyInterface> implementations) {
this.implementations = implementations;
}
#GetMapping("/run/{beanName}")
Object runSelectedImplementation(#PathVariable String beanName) {
return Optional.ofNullable(implementations.get(beanName))
.orElseThrow(UnknownImplementation::new)
.getStuff();
}
#ResponseStatus(BAD_REQUEST)
class UnknownImplementation extends RuntimeException {
}
}
}
It passes the following tests:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldCallImplementation1() throws Exception {
mockMvc.perform(get("/run/getFoo"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("foo")));
}
#Test
public void shouldCallImplementation2() throws Exception {
mockMvc.perform(get("/run/getBar"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("bar")));
}
#Test
public void shouldRejectUnknownImplementations() throws Exception {
mockMvc.perform(get("/run/getSomethingElse"))
.andExpect(status().isBadRequest());
}
}
Regarding two of your doubts :
1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.
2. You can use the exact Path mapping to get rid of switch case. For e.g. :
#GetMapping("/specificRequest/value1")
#GetMapping("/specificRequest/value2")
#GetMapping("/specificRequest/value3")
All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
Hope this will help to make code more cleaner and elegant.
There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :
Makes it easy to write the test cases.
Scaling the same without impacting any other source/service.
Your code dealing the each source as separate entity from other sources.
The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.

customizable aspects in spring

I’d like to apply a customizable aspect on two different services (spring bean). My problem is how/where to set/define the pointcut expression. One normally defines the pointcut expression on a ‘dummy method’ or directly on the advice method. However that means the pointcut is static (not customizable).
I’d like to define the pointcut at the bean creation level to be able to create the same kind of advice for different targets. Ideally I’d like to do something like this:
#Aspect
public class ServiceAspect {
private static final Logger LOG = LoggerFactory.getLogger(ServiceAspect.class);
private final String discriminator;
// no advice defined here!!!
public ServiceAspect(String discriminator) { this.discriminator = discriminator; }
public Object around(ProceedingJoinPoint jp) throws Throwable {
LOG.info(discriminator + " called");
return jp.proceed();
}
}
#Configuration
#EnableAspectJAutoProxy
#PropertySource("classpath:application.properties")
public class ServiceConfiguration {
#Bean
public MyService service1() { return new MyServiceImpl(); }
#Bean
#Around("bean(service1)") // define the advice when bean is created
#ConditionalOnProperty("aspect1Enbaled")
public ServiceAspect aspect() {
return new ServiceAspect("Aspect-1");
}
#Bean
public YourService service2() { return new YourServiceImpl(); }
#Bean
#Around("bean(service2)") // define a different advice when bean is created
#ConditionalOnProperty("aspect2Enbaled")
public ServiceAspect aspect() {
return new ServiceAspect("Aspect-2");
}
}
Notice that the #Around annotation is on the definition of the bean. I can thus reuse the aspect for different target. Using the #ConditionalOnProperty, this would enable me to turn on/off individual aspect base on a property.
Can anyone help me with this? I suspect I’ll need to create some kind of factory but can’t seem to see how I can REPLACE an already defined bean (the service bean) with a proxy!

Categories

Resources