Several beans implementating the same interface - spring

The exact usage is like this:
#Slf4j
public class Client<E, Key> {
#Getter #NonNull private final UpdateListener<E, Key> updateListener;
#NonNull private final SubscriptionFactory subscriptionFactory;
#NonNull private final Map<Key, Instant> updatedRegistry = new ConcurrentHashMap<>();
public Client(UpdateListener<E, Key> updateListener,
SubscriptionFactory subscriptionFactory) {
this.updateListener = updateListener;
this.subscriptionFactory = subscriptionFactory;
this.subscriptionFactory.registerSnapshotClient(updateListener);
log.info("Created new snapshot client for entity key [{}], update type [{}] and component qualifier [{}]",
updateListener.getEntityKey(),
updateListener.getOptionalChangeType(),
updateListener.getComponentQualifier());
}
#RabbitListener(queues = {"#{#queueNameCreator.createUpdateQueueName(snapshotClient.getUpdateListener())}",
"#{#queueNameCreator.createSnapshotQueueName(snapshotClient.getUpdateListener())}"})
public void handleMessage(Message<E> rawUpdate, #Header("last_updated") Instant newUpdatedTime) {
...//more code
}
}
Each 'Client' instance has its own bean id to not clash with each other.
How can I call get the exact updateListener of this object using SpEl?
Update
After using programattical approach and registering method I get the following exception:
Apr 28, 2015 3:22:47 PM org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler handleError
WARNING: Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener method 'public void com.everymatrix.om2020.messaging.model.SnapshotClient.handleMessage(org.springframework.messaging.Message<E>,java.time.Instant)' threw exception
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:126)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:93)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:756)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:679)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:167)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1241)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:660)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1005)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:989)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1103)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalStateException: No suitable resolver for argument [0] [type=org.springframework.messaging.Message]
Update
Done, you need to do the following to achieve the desired behaviour.
#Configuration
#EnableRabbit
public static class OmbeRabbitListenerConfigurer implements RabbitListenerConfigurer {
#Autowired ApplicationContext applicationContext;
#Autowired SnapshotClientQueueNamesCreator snapshotClientQueueNamesCreator;
#Autowired RabbitListenerContainerFactory rabbitListenerContainerFactory;
#Autowired MessageConverter messageConverter;
#Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
final Collection<SnapshotClient> snapshotClients = applicationContext.getBeansOfType(SnapshotClient.class).values();
System.out.println(snapshotClients);
snapshotClients.stream().forEach(bean -> {
final String snapshotQueueName = snapshotClientQueueNamesCreator.createSnapshotQueueName(bean.getUpdateListener());
final String updateQueueName = snapshotClientQueueNamesCreator.createUpdateQueueName(bean.getUpdateListener());
Method method = Stream.of(bean.getClass().getMethods()).filter(x -> x.getName().equals("handleMessage")).findAny().get();
MethodRabbitListenerEndpoint endpoint = new MethodRabbitListenerEndpoint();
final DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
messageHandlerMethodFactory.afterPropertiesSet();
endpoint.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
endpoint.setBean(bean);
endpoint.setMethod(method);
endpoint.setId(snapshotQueueName + ":" + updateQueueName + UUID.randomUUID());
endpoint.setQueueNames(snapshotQueueName, updateQueueName);
endpoint.setExclusive(false);
registrar.registerEndpoint(endpoint, rabbitListenerContainerFactory);
});
}
}

Your question is not clear - you seem to be mixing runtime and initialization time concepts.
For example, "#{#queueNameCreator.createUpdateQueueName(e.c.doSomething())}" is evaluated once during initialization - it's not clear from this expression what e is, or where it comes from.
But, you seem to be passing in an E in the payload of message: Message<E> rawUpdate. This message came from the queue and therefore can't influence the queue name.
Perhaps if you can explain what you are trying to do rather than how you have attempted to do it, I can update this "answer" with possible solutions.
EDIT:
If you mean you want to reference some field in the current (listener) bean in your SpEL then it can't be done directly.
EDIT2:
I can't think of any way to get a reference to the current bean in the SpEL expression - it has to be a constant; that's just the way annotations work in Java; they are tied to the class, not the instance.
I think to do what you want, you would need to revert to using programmatic endpoint registration. However, you'd need to wire in a MethodRabbitListenerEndpoint (rather than the SimpleRabbitListenerEndpoint) to get the benefits of the annotation you are looking for (#Header etc).
We don't really cover it in the documentation; it's a little advanced, but essentially, you need to inject the bean and Method (for the listener), and a DefaultMessageHandlerMethodFactory.

Related

SOLVED. Mock service.insert() works with ArgumentMatchers.any() only, triggers exception otherwise

Im creating a test class using Mockito and everything is runnig OK, except by one mock that calls a service layer method and it only works if the input parameter is any() and it throws the following exception if the input parameter is native from the method.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "com.devsuperior.dscatalog.dto.ProductDTO.getId()" because "productDTO" is null at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
Lets go through the code:
The service layer method, productDTO is a DTO class from a ordinary Product entity.
#Transactional
public ProductDTO insert(ProductDTO productDTO) {
Product entity = new Product();
copyDtoToEntity(productDTO, entity);
entity = repository.save(entity); // reposity.save() returns a reference to object saved in DB
return new ProductDTO(entity);
}
the controller layer method:
#PostMapping
public ResponseEntity<ProductDTO> insert(#RequestBody ProductDTO productDTO){
productDTO = service.insert(productDTO); //[1]
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(productDTO.getId()).toUri();
return ResponseEntity.created(uri).body(productDTO);
}
Before I continue I ran in debug mode this code and the ProdutDTO was correctly instanciated until the mock captured the service.insert(productDTO) call and after this line [1] productDTO = null
my test class:
#WebMvcTest(ProductResource.class)
public class ProductResourceTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService service;
#Autowired
private ObjectMapper objectMapper;
private PageImpl<ProductDTO> page;
private ProductDTO productDTO;
private long existingId;
private long nonExistingId;
private long dependentId;
private long productBadCategoryId;
private String jsonBody;
#BeforeEach
void setUp() throws Exception {
productDTO = Factory.createProductDTO();
existingId = Factory.getExistingProductId();
nonExistingId = 100L;
dependentId = 50L;
productBadCategoryId = 200L;
page = new PageImpl<>(List.of(productDTO));
when(service.findAllPaged(ArgumentMatchers.any())).thenReturn(page);
when(service.findById(existingId)).thenReturn(productDTO);
when(service.findById(nonExistingId)).thenThrow(ResourceNotFoundException.class);
when(service.update(eq(existingId), any())).thenReturn(productDTO);
when(service.update(eq(nonExistingId), any())).thenThrow(ResourceNotFoundException.class);
when(service.update(eq(productBadCategoryId), any())).thenThrow(NestedResourceNotFoundException.class);
doNothing().when(service).delete(existingId);
doThrow(ResourceNotFoundException.class).when(service).delete(nonExistingId);
doThrow(DatabaseException.class).when(service).delete(dependentId);
when(service.insert(any(ProductDTO.class))).thenReturn(productDTO); // IT WORKS! [2]
//when(service.insert( productDTO )).thenReturn(productDTO); // IT IT DOESNT WORKS! [3]
jsonBody = objectMapper.writeValueAsString(productDTO);
}
and finally the test method where the mock call triggers the exception
#Test
public void insertShouldReturnProductDTOCreated() throws Exception {
ResultActions result = mockMvc.perform(post("/products").content(jsonBody)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON));
result.andExpect(status().isCreated());
result.andExpect(jsonPath("$.id").exists());
result.andExpect(jsonPath("$.name").exists());
result.andExpect(jsonPath("$.description").exists());
}
Whenever I uncomment line [3] and comment line [2] the exception above is rised.
Of course I could let this way (its working), but If I want to raise an exception for the case the object to be inserted has some issue,I could build a new service.insert() mock to test the throw of the exception. As it is I can´t because I cant diferentiate a any() object from another one.I have read some similar problems like mine and the proposed solution was to add #Autowired annotation with service variable, but in my case still the issue remains.
The solution and a tentative of explanation what was happening:
Override HashCode/Equals methods in ProducDTO class.
When serializing ProductDTO class (this line-> jsonBody = objectMapper.writeValueAsString(productDTO) ) and upon reception on resource layer it turns again into an object but its new reference address it is not the same and for instance the non override equals will fail (it compares the reference address and not their content).

Spring Autowire configuration in flink

i am trying to use the comination of flink and springboot and im having some problems.
Lets say i am having this flow.
Getting json string that have one field date that contains date string.
using map function and ObjectMapper to parse it into object of LocalDateTime
print
This is simple usecase that will describe my probem.
So, i have Word Class represnting Word that contains LocalDateTime field.
#Data
public class Word {
#JsonDeserialize(using = LocalDateTimeSerde.class)
LocalDateTime date;
}
The LocalDateTimeDeserlization is looking like that(I want to autowire the app configuration):
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#JsonComponent
public class LocalDateTimeSerde extends JsonDeserializer<LocalDateTime> {
private final AppConf conf;
#Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(this.conf.getDateFormatter());
return LocalDateTime.parse(jsonParser.getText(), formatter);
}
}
AppConf.java represneting the configuration of the application is:
#Data
#Configuration
#ConfigurationProperties(value = "app")
public class AppConf {
private String dateFormatter;
}
DemoApplication.java:
final StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment(1);
String example = "{\"date\":\"2019-01-29 00:00\"}";
var stream = env
.fromElements(example)
.map(x->new ObjectMapper().readValue(x,Word.class))
.returns(Word.class);
stream.print();
env.execute("Demo App");
The exception im getting is :
Caused by: java.lang.IllegalArgumentException: Class com.example.demo.LocalDateTimeSerde has no default (no arg) constructor
The main problem here is that the code of the deserialization is running on the TaskManager and over there springboot doesnt take a part, so it doesn`t inject AppConf into the class.
Adding #NoArgsConstructor will not solve the problem
I think i know why it is hapenning (because flink master serialize the classes to the workers and then springboot doesn`t "ScanComponents" and takes control.
Is there any solution for that? I really want to combine spring with flink also in the worker`s function.
Thanks.
In general, I personally don't think it's a good idea to mix those concepts. The easiest solution is to use AutoWired only on the job manager and use explicit dependency injection when you go into Flink-land.
For example, you could extract the date pattern in the DemoApplication and set it on the ObjectMapper. (Don't forget to initialize ObjectMapper only once in your real code!)
If you really want to use AutoWiring. I guess you need to manually trigger the autowiring on taskmanager. There is a related post specifically for ObjectMapper.

OData (Olingo) "inhibit" endpoint

My question is about what is best way to inhibit an endpoint that is automatically provided by Olingo?
I am playing with a simple app based on Spring boot and using Apache Olingo.On short, this is my servlet registration:
#Configuration
public class CxfServletUtil{
#Bean
public ServletRegistrationBean getODataServletRegistrationBean() {
ServletRegistrationBean odataServletRegistrationBean = new ServletRegistrationBean(new CXFNonSpringJaxrsServlet(), "/user.svc/*");
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("javax.ws.rs.Application", "org.apache.olingo.odata2.core.rest.app.ODataApplication");
initParameters.put("org.apache.olingo.odata2.service.factory", "com.olingotest.core.CustomODataJPAServiceFactory");
odataServletRegistrationBean.setInitParameters(initParameters);
return odataServletRegistrationBean;
} ...
where my ODataJPAServiceFactory is
#Component
public class CustomODataJPAServiceFactory extends ODataJPAServiceFactory implements ApplicationContextAware {
private static ApplicationContext context;
private static final String PERSISTENCE_UNIT_NAME = "myPersistenceUnit";
private static final String ENTITY_MANAGER_FACTORY_ID = "entityManagerFactory";
#Override
public ODataJPAContext initializeODataJPAContext()
throws ODataJPARuntimeException {
ODataJPAContext oDataJPAContext = this.getODataJPAContext();
try {
EntityManagerFactory emf = (EntityManagerFactory) context.getBean(ENTITY_MANAGER_FACTORY_ID);
oDataJPAContext.setEntityManagerFactory(emf);
oDataJPAContext.setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
return oDataJPAContext;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
...
My entity is quite simple ...
#Entity
public class User {
#Id
private String id;
#Basic
private String firstName;
#Basic
private String lastName;
....
Olingo is doing its job perfectly and it helps me with the generation of all the endpoints around CRUD operations for my entity.
My question is : how can I "inhibit" some of them? Let's say for example that I don't want to enable the delete my entity.
I could try to use a Filter - but this seems a bit harsh. Are there any other, better ways to solve my problem?
Thanks for the help.
As you have said, you could use a filter, but then you are really coupled with the URI schema used by Olingo. Also, things will become complicated when you have multiple, related entity sets (because you could navigate from one to the other, making the URIs more complex).
There are two things that you can do, depending on what you want to achieve:
If you want to have a fined grained control on what operations are allowed or not, you can create a wrapper for the ODataSingleProcesor and throw ODataExceptions where you want to disallow an operation. You can either always throw exceptions (i.e. completely disabling an operation type) or you can use the URI info parameters to obtain the target entity set and decide if you should throw an exception or call the standard single processor. I have used this approach to create a read-only OData service here (basically, I just created a ODAtaSingleProcessor which delegates some calls to the standard one + overridden a method in the service factory to wrap the standard single processor in my wrapper).
If you want to completely un-expose / ignore a given entity or some properties, then you can use a JPA-EDM mapping model end exclude the desired components. You can find an example of such a mapping here: github. The mapping model is just an XML file which maps the JPA entities / properties to EDM entity type / properties. In order for olingo to pick it up, you can pass the name of the file to the setJPAEdmMappingModel method of the ODataJPAContext in your initialize method.

How to replace a constructor injected object with mocked object in spock

I know that the question is very big but I just want to clear the situation i am into.
I am working on an application that consumes the JMS messages from the message broker.
We are using camel route on the consumer side. All the object required in route builder are injected through constructor injection using spring .
I want to mock the behavior of the actual processing, Once the consumer receives the message from the queue. All the classes gets loaded via the spring configuration.
Below are the three classes:
CustomRouteBuilder.java
public CustomRouteBuilder extends RouteBuilder{
private CustomRouteAdapter customAdapter;
public CustomRouteBuilder (CustomRouteAdapter customAdapter){
this.customAdapter = customAdapter
}
public void configure(RouteDefinition route){
route.bean(customAdapter);
}
}
CustomRouteAdapter.java
public class CustomRouteAdapter {
private Orchestrator orchestrator;
public CustomRouteAdapter (Orchestrator orchestrator){
this.orchestrator = orchestrator;
}
#Handler
public void process(String message){
orchestrator.generate(message) ;
}
}
Orchestrator.java
public class Orchestrator{
private Service service;
public Orchestrator(Service service){
this.service = service;
}
public void generateData(String message){
service.process(message);
}
}
As per our requirement we have to load this configuration file and then write the functional test using spock.
Below is my
CustomRouteBuilderTest.groovy file.
import org.springframework.test.util.ReflectionTestUtils
import spock.lang.Specification
#ContextConfiguration(classes=[CustomRouteBuilderTest.Config.class])
class CustomRouteBuilderTest extends Specification{
private static final String message = "Hello";
Orchestrator orchestrator;
#Autowired
CustomRouteAdapter customRouteAdapter;
def setup(){
orchestrator = Mock(Orchestrator)
ReflectionTestUtils.setField(customRouteAdapter,"orchestrator",orchestrator)
orchestrator.generate(message )
}
private String getMessageAsJson() {
//return json string;
}
private String getMessage() {
// return message;
}
private Map<String, Object> doMakeHeaders() {
//Create message headers
}
private void doSendMessage(){
Thread.sleep(5000)
Map<String,Object> messageHeader = doMakeHeaders()
byte [] message = getMessageAsJson().getBytes()
CamelContext context = new DefaultCamelContext()
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(jmsBrokerUrl)
context.addComponent("activeMQComponent",JmsComponent.jmsComponent(connectionFactory))
ProducerTemplate template = context.createProducerTemplate()
context.start();
template.sendBodyAndHeaders("queueName", message, messageHeader)
}
def "test message consumption"(){
given:
doSendMessage()
}
#Configuration
#Import([FunctionalTestCommonConfig.class,CustomRouteBuilderConfig.class])
#PropertySource(value="classpath:test.properties")
static class Config{
}
}
The problem that here is even though I inject the mocked object to the adapter using ReflectionTestUtils , I am not able to define its behavior correctly.
And when the message is received the orchestrator tries to process it.
My Requirement is that:
Adapter should be called from the camel route automatically which happens but
when the orechestrator.generate is called from the adapter then nothing should happen it should simply return.
But here nothing like that is going on.
Each time I send a message the consumer(RouteBuilder) receives it and calls the handler function which then calls the
orchestrator.generate(message)
function and the orchestrator starts processing and throws an exception from service level.
Any one can please help me on this.
I suppose your beans have been proxified by Spring, and this proxy use cglib (because you see CustomRouteBuilder$$EnhancerBySpringCGLIB$$ad2783ae).
If it's really the case, you didn't #Autowired in your test the real instance of your CustomRouteAdapter but a cglib proxy: Spring creates a new class, extending the realclass, and overriding all the methods of this class. The new method delegate to the real instance.
When you change the orchestrator field, you are in reality changing the orchestrator field of the proxy, which is not used by the real instance.
There are severals ways to achieve what you want to do:
add a setOrchestrator method in CustomRouteAdapter
create the mock in your spring configuration and let spring inject this mock instead of a real instance of Orchestrator
Inject the orchestrator in the real instance (ugly - I didn't recommend you that, it didn't help in the testability of your code!)
customRouteAdapter.targetSource.target.orchestrator = _themock_

Getting LazyInitializationException within in a #Transactional method

I’m getting this exception when I access this method from my controller:
{"status":"failure","exception":"LazyInitializationException","exceptionMessage":"failed
to lazily initialize a collection of role:
org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not
initialize proxy - no Session","errorMessage":"failed to lazily
initialize a collection of role:
org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not
initialize proxy - no Session"}
Controller:
#Autowired
private ThirdPartyService m_thirdPartySvc;
…
#RequestMapping(value = "/launch", method = RequestMethod.GET)
#Transactional
public String launchLti(final #RequestParam String assignmentId,
final Model model,
final HttpServletRequest request,
final HttpServletResponse response,
final Principal principal) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException
{
final subcoAuthenticationUser auth = (subcoAuthenticationUser) ((Authentication) principal).getPrincipal();
String nextPage = null;
final User user = m_userSvc.findById(auth.getId());
// Provision the assignment in ThirdParty if not already done so
final Assignment assmt = m_lessonPlanDao.getAssignment(assignmentId);
if (!assmt.isSentToThirdParty())
{
m_thirdPartySvc.sendAssignment(assignmentId);
} // if
Is the #Transactional annotation unnecessary? Especially since I already have it on my #Service class…
#Service
#Transactional
public class ThirdPartyServiceImpl implements ThirdPartyService
{
#Override
public void sendAssignment(final String assignmentId)
{
final Assignment assignment = m_lessonPlanDao.getAssignment(assignmentId);
if (isThirdPartyAssignment(assignment))
{
final String ThirdPartyPromptId = assignment.getTocItem().getThirdPartyPromptId();
// Gather the teacher id
final LessonPlan lessonPlan = m_lessonPlanDao.getLessonPlan(assignment.getLessonPlan().getId());
final String teacherId = lessonPlan.getOwnerId();
// Gather the students who have been assigned this assignment
final List<Classroom> classes = lessonPlan.getClassrooms();
// Send one request for each class assignment
for (final Classroom classroom : classes)
{
The error occurs on the for (final Classroom classroom : classes) line. I have #Transactional everywhere, yet I’m getting this LazyInitializationException. Why? And how do I create a transaction so that I can run my method?
I’m using Spring 3.2.11.RELEASE, Hibernate 4.3.6.Final, and JPA 2.1 on JBoss 7.1.3.Final. If upgrading any of these would solve my problem, let me know.
The #Transactional boundary is being respected by application during runtime. You can find this out by calling: TransactionSynchronizationManager#isActualTransactionActive()
Add some code to print out the value of above method. If it's false, then maybe you need to make sure the component-scan is set up right.
Example: <context:component-scan base-package="com.application.dao" />
This would totally miss the classes in the com.application.service package.

Resources