I'm using Spring Cloud Stream with RabbitBinder for pub/sub message processing.
Events emitted from services are sent via the broker. The event class structure is polymorphic, i.e. there is
abstract class OrderEvent {
abstract String type();
}
class OrderCreatedEvent extends OrderEvent {
#Override
String type() {
return "ORDER_CREATED";
}
...
}
abstract class OrderLineEvent extends OrderEvent {
...
}
class OrderLineCreatedEvent extends OrderLineEvent {
...
}
I am publishing events via StreamBridge,
#Override
public void publish(List<OrderEvent> events) {
events.forEach(e -> {
streamBridge.send("order-out-0", e);
});
}
and consuming via reactive consumer
#Bean
public Consumer<OrderEvent> order() {
return orderEvent -> {
System.out.println("got order event" + orderEvent);
};
}
But how to properly deserialize polymorphic types? content-type is set to application/json.
The code above does not work, it throws IllegalArgumentException, can not cast to OrderEvent.
If I restrict the code to publish OrderCreatedEvent (or any other leaf class) only, and adjust the consumer to OrderCreatedEvent the events are received successfully.
Thanks for your help!
Kind regards,
Andreas
There are a couple of disadvantages of using an Event class hierarchy. This is one. The other is that you share event types across distributed components which requires upgrading all dependent components whenever you add a new event type. I prefer using a single type with an event type field as a string.
Related
Spring Data REST has the following Event handlers which are fired on HTTP requests like POST, PUT etc.
#RepositoryEventHandler(Author.class)
public class AuthorEventHandler {
Logger logger = Logger.getLogger("Class AuthorEventHandler");
#HandleBeforeCreate
public void handleAuthorBeforeCreate(Author author){
logger.info("Inside Author Before Create....");
}
#HandleAfterCreate
public void handleAuthorAfterCreate(Author author){
logger.info("Inside Author After Create ....");
}
}
My question is if I access another database entity, eg.Book, within #HandleBeforeCreate and modify it, would it occur in a separate transaction or will it occur in the same transaction as the creation of the Author entity?
I already check the Spring Data REST docs but it is not mentioned there.
From my experience, those handlers are performed beyond the main transaction. Literally 'before' and 'after' it. About 'separate' transaction - if you mark your event handler as #Transactional it will be executed in its individual transaction.
Publishing domain events from the aggregate root
If you need to perform some extra actions within the main transaction you can use publishing events from the aggregate root. In this case, you should extend your entity from AbstractAggregateRoot and add to it some methods that register appropriate events, for example:
#Entity
public class Model extends AbstractAggregateRoot {
// entity stuff...
public Model initExtraAction(SomeData payload) {
registerEvent(new ExtraActionEvent(this, payload));
return this;
}
}
where registerEvent is the AbstractAggregateRoot method, and ExtraActionEvent is your custom event, like the folowing:
#Value
public class ExtraActionEvent {
private Model model;
private SomeData payload;
}
Then you can implement an ordinary event handler
#Service
public class EventHandler {
#EventListener
#Transactional(propagation = MANDATORY) // optional
public void handleExtraActionEvent (ExtraActionEvent e) {
Model model = e.getModel();
SomeData payload = e.getPayload();
// Extra actions...
}
}
that will be called in the same transaction as the main one (which saves your entity) if you call initExtendAction method before invoking the save method of your repo (to make sure that this will be done in the same transaction you can use an optional #Transactional(propagation = MANDATORY) annotation):
modelRepo.save(model.initExtraAction(payload));
In the Spring Data REST project we can call initExtraAction method in the 'RepositoryEventHandler' before the entity will be created or updated:
#RepositoryEventHandler(Model.class)
public class ModelEventHandler {
#HandleBeforeCreate
#HandleBeforeSave
public void handleBeforeCreateOrSave(Model model){
// Some manipulations...
model.initExtraAction(...);
}
}
You can find a full example of using AbstractAggregateRoot in the Oliver Gierke Spring RestBucks demo project.
Additional info: DDD Aggregates and #DomainEvents
I'm using spring-integration-kafka.
I have an abstract interface Event, which has dozens of concrete implementations, say, AEvent, BEvent, CEvent, etc.. And I want one only consumer listener to handle all incoming Events, such as fun handle(message: Message<>) { message.payload... }
After reading the documentation, I find no way to support auto-deserialization with no explicit type provided in consumer side.
Any suggestion will be appreciated.
Using JdkSerializationRedisSerializer from spring-data-redis meet my requirements.
public class GenericObjectSerializer implements Serializer<Object> {
private final JdkSerializationRedisSerializer serializer =
new JdkSerializationRedisSerializer();
}
public class GenericObjectDeserializer implements Deserializer<Object> {
private final JdkSerializationRedisSerializer serializer =
new JdkSerializationRedisSerializer();
}
I have a Spring application that consumes a SOAP web services. I have several classes that are quite simple and only differ in the #XmlRootElement. I'm wondering if there's a way to create a more generic class that I can set the root element on dymanically.
Here's a few of the classes with only the root element being different.
#XmlRootElement(name="safetydate")
public class SafetyDateRequest extends Carrier411RequestImpl {
}
#XmlRootElement(name="checkallsafety")
public class SafetyGetAllRequest extends Carrier411RequestImpl {
}
#XmlRootElement(name="checksafetyupdates")
public class SafetyGetUpdatesRequest extends Carrier411RequestImpl {
}
In another class, I'm processing these classes in the following fashion:
private void sendRequest(Carrier411Request request, Carrier411ResponseHandler responseHandler) throws FaultCodeException {
Carrier411Response response = (Carrier411Response) ws.marshalSendAndReceive(registry.get(request.getClass()), request);
checkResponseForFault(response);
responseHandler.handleResponse(request, response);
}
I know there's another version of marshalSendAndReceive that accepts a callback allowing you to modify the request before actually sending it, but I haven't figured out how to achieve what I'm trying to do.
I've the following bundles:
- GreetingAPI (bundle which defines the greeting() method) (Service)
- GreetingImpl1 (bundle which implements greeting() method for English mode)
- GreetingImpl2 (bundle which implements greeting() method for Italian mode)
- GreetingConsumer (bundle which uses the greeting service)
How Can I create a component (I suppose it's a factory) that based on a given language parameter lets the consumer bundle to use a different implementation of the service.
You're thinking about this the wrong way around. The provider should not register a different service depending on something that the consumer does, because the provider shouldn't know anything about the consumer.
Instead, you can have multiple providers of the same service but annotate them with appropriate metadata. Then the consumer of the service can choose whether or not to filter on specific properties.
For example, when we register a service we can add properties as follows (note that I am using the OSGi Declarative Services annotations, see OSGi Compendium Release 5, section 112.8):
#Component(property = "locale=en_GB")
public class MyGreetingImpl1 implements Greeting {
public String greet() { return "How do you do"; }
}
#Component(property = "locale=en_US")
public class MyGreetingImpl2 implements Greeting {
public String greet() { return "Howdy"; }
}
#Component(property = "locale=fr_FR")
public class MyGreetingImpl3 implements Greeting {
public String greet() { return "Bonjour"; }
}
Now the consumer can choose whichever language it wants using a target filter. Note the use of a wildcard, as the consumer in this case only cares about the language but not the country code:
#Component
public class GreetingConsumer {
#Reference(target = "(language=en*)")
public void setGreeting(Greeting greeting) { ... }
}
One of the possible solutions is to have some kind of a language manager. So your consumer has a language manager and not directly the greetings service
The manager is notified with the registration / deregistration of every language implementation of your GreetingAPI.
Your language manager keeps trace of the diffrent implementations. Your manager provides the right implementation of the target language (using an enum for example)
Example
public class LanguageManagerImpl implements LanguageManager {
//LanguageEnum can be used to distinguish the different languages
private Map<LanguageEnum, GreetingAPI> greetings = new HashMap<LanguageEnum, GreetingAPI>();
public void registerLanguage(GreetingAPI greeting) {
LanguageEnum language = greeting.getLanguageEnum();
//add the greetings to the map
}
public void deregisterLanguage(GreetingAPI greeting){
//remove your greeting from the map
}
public GreetingAPI getGreetingForLanguage(LanguageEnum language) {
return greetings.get(language);
}
}
If you are using blueprint, then you need to add to the language manager blueprint a reference-list with a refence listener on GreetingAPI
Otherwise you use traditional listeners
Example
<bean id="languageManager"
class="your.language.managerimpl.LanguageManagerImpl">
</bean>
<service ref="languageManager" interface="your.language.manager.interface.LanguageManager" />
<reference-list interface="your.greeting.interface.GreetingAPI"
availability="optional">
<reference-listener ref="languageManager"
bind-method="registerLanguage" unbind-method="deregisterLanguage" />
</reference-list>
I am developing a demo application using Spring MVC v3.0 and the Jdbc template.In my application for different -2 module we need some same methods as save,update,delete etc.. .So instead of writing again and again same method for different modules.Do we have any way to implement this kind of functionality in a common class(abstract class).
Hope some buddy will give me the good way to learn and implement this functionality.
You can do that by having a super class with a save method that takes Object type parameter and then you have to check instanceOf and implement.But i suggest you to have different methods for different type.
create an Abstract class
public abstract class AbstractDaoImpl<E,F> extends HibernateDaoSupport{
public abstract Class<E> getEntityType();
public void update(Object updateObject) throws DAOException {
try {
getHibernateTemplate().saveOrUpdate(updateObject);
getHibernateTemplate().flush();
}catch(Exception ex){
logger.error("Error updating attachment: " + ex.getMessage());
throw new DAOException(ex.getMessage(),Code.DAO_EXCEPTION);
}finally {}
//To find by ID
#SuppressWarnings("unchecked")
#Override
public E retrieveSingleMatch(F id) {
return (E) getHibernateTemplate().get(getEntityType(), (Serializable) id);
}
}
and the Dao Implementations
public class StudentDaoImpl<Student,String> extends AbstractDaoImpli implements MyDao {
#SuppressWarnings("unchecked")
public Class getEntityType() {
return Student.class;
}
}
Your service code will be
studentDao.update(anyDomainObject);
Student student = studentDao.retrieveSingleMatch(studentId);
depending on your object ,