#Gateway is not required if we use #MessagingGateway - spring

From Spring 4.0 onwards #MessagingGateway was introdued. Using it if we have only one gateway method in our Gateway interface , then we don't need to annotate the Gateway method with #Gateway.
Below is my example, where both are working.
So, my question is can we stop using #Gateway when we have only one method in Gateway interface?
Code-1:
#MessagingGateway(name="demoGateway")
public interface DemoGateway {
#Gateway(requestChannel = "gatewayRequestChannel",replyChannel = "nullChannel")
void accept(Message<String> request);
}
Code-2:
#MessagingGateway(name="demoGateway",defaultRequestChannel =
"gatewayRequestChannel",defaultReplyChannel = "nullChannel")
public interface DemoGateway {
void accept(Message<String> request);
}

Yes. You are right. You can do approach 2 and leave the single method that confirms to the default configuration of #MessagingGateway without annotation.
However in practice, I will only move the truly default values to the MessagingGateway and leave other values to #Gateway annotation.
This is because it makes life and readability easier in the future if you have to add more methods to DemoGateway in the future.

Related

Spring Cloud Stream 3 RabbitMQ consumer not working

I'm able to make Spring+Rabbit work with the non-functional way (prior to 2.0?), but I'm trying to use with the functional pattern as the previous one is deprecated.
I've been following this doc: https://docs.spring.io/spring-cloud-stream/docs/3.1.0/reference/html/spring-cloud-stream.html#_binding_and_binding_names
The queue (consumer) is not being created in Rabbit with the new method. I can see the connection being created but without any consumer.
I have the following in my application.properties:
spring.cloud.stream.function.bindings.approved-in-0=approved
spring.cloud.stream.bindings.approved.destination=myTopic.exchange
spring.cloud.stream.bindings.approved.group=myGroup.approved
spring.cloud.stream.bindings.approved.consumer.back-off-initial-interval=2000
spring.cloud.stream.rabbit.bindings.approved.consumer.queueNameGroupOnly=true
spring.cloud.stream.rabbit.bindings.approved.consumer.bindingRoutingKey=myRoutingKey
which is replacing:
spring.cloud.stream.bindings.approved.destination=myTopic.exchange
spring.cloud.stream.bindings.approved.group=myGroup.approved
spring.cloud.stream.bindings.approved.consumer.back-off-initial-interval=2000
spring.cloud.stream.rabbit.bindings.approved.consumer.queueNameGroupOnly=true
spring.cloud.stream.rabbit.bindings.approved.consumer.bindingRoutingKey=myRoutingKey
And the new class
#Slf4j
#Service
public class ApprovedReceiver {
#Bean
public Consumer<String> approved() {
// I also saw that it's recommended to not use Consumer, but use Function instead
// https://docs.spring.io/spring-cloud-stream/docs/3.1.0/reference/html/spring-cloud-stream.html#_consumer_reactive
return value -> log.info("value: {}", value);
}
}
which is replacing
// BindableApprovedChannel.class
#Configuration
public interface BindableApprovedChannel {
#Input("approved")
SubscribableChannel getApproved();
}
// ApprovedReceiver.class
#Service
#EnableBinding(BindableApprovedChannel.class)
public class ApprovedReceiver {
#StreamListener("approved")
public void handleMessage(String payload) {
log.info("value: {}", payload);
}
}
Thanks!
If you have multiple beans of type Function, Supplier or Consumer (which could be declared by third party libraries), the framework does not know which one to bind to.
Try setting the spring.cloud.function.definition property to approved.
https://docs.spring.io/spring-cloud-stream/docs/3.1.3/reference/html/spring-cloud-stream.html#spring_cloud_function
In the event you only have single bean of type java.util.function.[Supplier/Function/Consumer], you can skip the spring.cloud.function.definition property, since such functional bean will be auto-discovered. However, it is considered best practice to use such property to avoid any confusion. Some time this auto-discovery can get in the way, since single bean of type java.util.function.[Supplier/Function/Consumer] could be there for purposes other then handling messages, yet being single it is auto-discovered and auto-bound. For these rare scenarios you can disable auto-discovery by providing spring.cloud.stream.function.autodetect property with value set to false.
Gary's answer is correct. If adding the definition property alone doesn't resolve the issue I would recommend sharing what you're doing for your supplier.
This is also a very helpful general discussion for transitioning from imperative to functional with links to repos with more in depth examples: EnableBinding is deprecated in Spring Cloud Stream 3.x

Configuring Spring MockMvc to use custom argument resolver before built-in ones

I have a straightforward test case. I have a controller which has a parameter of a type Spring doesn't support by default, so I wrote a custom resolver.
I create the mock mvc instance I'm using like so:
mvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new GoogleOAuthUserResolver()).build();
However, Spring is also registering almost 30 other argument resolvers, one of which is general enough that it is getting used to resolve the argument before mine. How can I set or sort the resolvers so that mine is invoked first?
This worked for me without reflection:
#RequiredArgsConstructor
#Configuration
public class CustomerNumberArgumentResolverRegistration {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#PostConstruct
public void prioritizeCustomArgumentResolver () {
final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(Objects.requireNonNull(requestMappingHandlerAdapter.getArgumentResolvers()));
argumentResolvers.add(0, new CustomerNumberArgumentResolver());
requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);
}
}
The issue was that the People class the Google OAuth library I am using extends Map and the mock servlet API provides no way to manipulate the order in which the handlers are registered.
I ended up using reflection to reach into the mocks guts and remove the offending handler.

Spring data rest - Is there a way to restrict the supported operations?

I want to expose data from a database as Restful APIs in a Spring(SpringBoot) application. Spring Data Rest appears to be an exact fit for purpose for this activity.
This database is read-only for my application needs. The default provides all the HTTP methods. Is there a configuration that I can use to restrict (in fact prevent) the other methods from being exposed?
From the Spring docs on Hiding repository CRUD methods:
16.2.3. Hiding repository CRUD methods
If you don’t want to expose a save or delete method on your
CrudRepository, you can use the #RestResource(exported = false)
setting by overriding the method you want to turn off and placing the
annotation on the overriden version. For example, to prevent HTTP
users from invoking the delete methods of CrudRepository, override all
of them and add the annotation to the overriden methods.
#RepositoryRestResource(path = "people", rel = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
#Override
#RestResource(exported = false)
void delete(Long id);
#Override
#RestResource(exported = false)
void delete(Person entity);
}
It is important that you override both delete methods as the exporter
currently uses a somewhat naive algorithm for determing which CRUD
method to use in the interest of faster runtime performance. It’s not
currently possible to turn off the version of delete which takes an ID
but leave exported the version that takes an entity instance. For the
time being, you can either export the delete methods or not. If you
want turn them off, then just keep in mind you have to annotate both
versions with exported = false.
As of early 2018, there is now the ability to only expose repository methods explicitly declared for exposure (DATAREST-1176)
See RepositoryRestConfiguration
A Export false at Type level does not allow overriding with export true at Method level ticket (DATAREST-1034) was opened, but closed as a duplicate of DATAREST-1176. Oliver Gierke stated:
I'll resolve this as fixed against the version of DATAREST-1176 for
now but feel free to reopen in case there's anything else you need.
They are not exact duplicates and the functionality described in 1034 would have been more user friendly, but there are at least some options now.
By default, Spring boot exposes all methods to REST. You can set that to false.
config.setExposeRepositoryMethodsByDefault(false);
For more information, you can refer org.springframework.data.rest.core.config.RepositoryRestConfiguration.
Sample code snippet to do this:
#Configuration
public class ApplicationRepositoryConfig implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
..........
config.setExposeRepositoryMethodsByDefault(false);
}
}
Since Spring Data REST 3.1, we can configure exposure per HTTP method. I used the following snippet to disable exposure of PUT, PATCH, POST and DELETE methods for items and collections:
#Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
ExposureConfiguration exposureConfiguration = config.getExposureConfiguration();
exposureConfiguration.withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PUT)
.disable(HttpMethod.PATCH).disable(HttpMethod.POST).disable(HttpMethod.DELETE))
.withCollectionExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PUT)
.disable(HttpMethod.PATCH).disable(HttpMethod.POST).disable(HttpMethod.DELETE));
}
}

Mocking a constructor of #Autowired service(system under test)

I have to mock jerseyclient which is being created in Constructor of subjected service. Subjected service is System under test injected via Spring's #Autowired.
In constructor of the service client=client.create() method is written. We can't change this code(Although this is a code smell). I want to mock the jersey client but it is in constructor of the service. I am not able to mock this
sooo... long story short.. admitting you use mockito, in your src for test you should have an applicationcontext for your test.. usually we define one programmatically so, something along those lines..
import the .xml file you use for test purpose (in my case i imported the one for the mailserver, for the connection and for the authentication) instead of the one i use for the "local" environmnet. After then define a method to setup each and every of your service.
You might need to add a mock for your template resolver as well, but ultimately this all depends on your stack...
So based on your approach the final thing might be a bit different, but ultimately you're gonna do something along the lines of what i outline below:
#Configuration
#ImportResource(value = {
"classpath:applicationContext-jdbc-test.xml",
"classpath:applicationContext-ldap-test.xml",
"classpath:applicationContext-mail-test.xml"})
public class ApplicationTestContext {
#Bean
public ObjectMapperWrapper objectMapperWrapper() {
return Mockito.mock(ObjectMapperWrapper.class);
}
#Bean
public YourService yourService() {
return new YourServiceImpl();
}
}

Why does Spring allow controller annotated request mappings on private methods?

Just came accross this today in a Spring MVC cotnroller class,
#RequestMapping(value = { "/foo/*" }, method = { RequestMethod.GET})
private String doThing(final WebRequest request) {
...
return "jsp";
}
This is making it a bit harder to write a test, I'll probably change it to public but what's the point of allowing mappings on private methods?
Java does not provide a mechanism for limiting the target of annotations based on access modifier.
As #smp7d stated, Java does not limit the target of annotations based on access modifiers, but syntactically speaking, #RequestMapping should not work on private methods. Also we cannot limit this, since it would break the backward compatibility. So, you can either go for defining your methods as public or you can create your own custom implementation.
Take a look at this: Spring's #RequestMapping annotation works on private methods

Resources