I have an eventsourced aggregate and use Jackson as eventserializer. Now, when I apply an event A in a command handler, I can see it's event sourcing handler called immediately, with all the expected event fields (event is the same instance as I created in the command handler). One of the fields in the event is the aggregateId.
However, when the (read side) event handler is called, the event object is a different instance, but the field I filled with the aggregateId has a wrong value! Debugging shows it is filled with the event identifier. When I set a breakpoint in the event constructors, I see it called and a wrong field value being set.
When I switch to XStream as event serializer, everything is fine. No additional even instantion is done, and the event created in the command handler is the same as being processing in the eventhandler.
What is going on here?
After an hour of debugging, I found my own mistake ;-). As the Axon docs say, when using Jackson as EventSerializer, you have to stick to the Jackson conventions, which I didn't. All my aggregate id's are subclasses of this AggregateId:
public abstract class AggregateId {
private final UUID id;
public AggregateId() {
this(UUID.randomUUID());
}
public AggregateId(UUID id) {
this.id = id;
}
public String toString() {
return id.toString();
}
public UUID getValue() {
return id;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AggregateId that = (AggregateId) o;
return id.equals(that.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
Having a private field id with no getter and a getValue that returns that id is the perfect way to fool Jackson (and myself).
After renaming id to value all tests are green.
Related
I am getting the below error. Axon version 3.3.3
org.axonframework.eventsourcing.IncompatibleAggregateException:
Aggregate identifier must be non-null after applying an event. Make
sure the aggregate identifier is initialized at the latest when
handling the creation event.
I have created a UserAggregate. It contains 2 events:
UserCreated
UpdateUserEvent
I am able to generate the first (UserCreated) event and it was saved in the event store with sequence 0, But while generating the second event I got the above-mentioned error.
Any suggestions on this?
UserAggregate.java
#Aggregate
public class UserAggregate {
#AggregateIdentifier
private String id;
private String email;
private String password;
public UserAggregate(String id, String email, String password) {
super();
this.id = id;
this.email = email;
this.password = password;
}
#CommandHandler
public UserAggregate(CreateUser cmd) {
AggregateLifecycle.apply(new UserCreated(cmd.getId(), cmd.getEmail(), cmd.getPassword()));
}
#CommandHandler
public void handle(UpdateUserCmd cmd) {
AggregateLifecycle.apply(new UpdateUserEvent(cmd.getId(), cmd.getEmail(),""));
}
#EventSourcingHandler
public void userCreated(UserCreated event) {
System.out.println("new User: email " + event.getEmail() +" Password: "+ event.getPassword());
setId(event.getId());
}
#EventSourcingHandler
public void updateUserEvent(UpdateUserEvent event) {
System.out.println("new User: email " + event.getEmail());
setId(event.getId());
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public UserAggregate() {
}
}
I am still getting to know Axon but here's how I managed to resolve the issue. Basically what the error is saying is that, when the UserAggregate is being instantiated the aggregate identifier (aka Primary Key) must not be null and must have a value.
There sequence of the life-cycle is that
It calls a no args constructor
It calls the constructor with your initial command, in your case. At this point, your aggregate identifier is still null and we will assign a value in the next step
It then calls a EventSourcingHandler that handles the event your applied from the previous step
Based on the steps above here's what you need to do:
Create a no args constructor
protected UserAggregate() {
}
Create a constructor which handles your first command:
#CommandHandler
public UserAggregate(CreateUser cmd) {
AggregateLifecycle.apply(
new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
}
Finally add an event sourcing handler to handle the UserCreated event
#EventSourcingHandler
public void on(UserCreated userCreated) {
// this is where we instantiate the aggregate identifier
this.id = userCreated.getId();
//assign values to other fields
this.email = userCreated.getEmail();
this.password = userCreated.getPassword();
}
And here's the complete example:
#Aggregate
public class UserAggregate {
#AggregateIdentifier
private String id;
private String password;
private String email;
protected UserAggregate() {
}
#CommandHandler
public UserAggregate(CreateUser cmd) {
AggregateLifecycle.apply(
new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
}
#EventSourcingHandler
public void on(UserCreated userCreated) {
// this is where we instantiate the aggregate identifier
this.id = userCreated.getId();
//assign values to other fields
this.email = userCreated.getEmail();
this.password = userCreated.getPassword();
}
}
When you are following the Event Sourcing paradigm for your Aggregates, I'd typically suggest two types of constructors to be present in the code:
A default no-arg constructor with no settings in it.
One (or more) constructor(s) which handles the 'Aggregate creation command'
In your example I see a third constructor to set id, email and password.
My guess is that this constructor might currently obstruct the EventSourcedAggregate implementation for correct validation.
The exception you are receiving can only occur if the #AggregateIdentifier annotated field is not set after the constructor command handler (in your case UserAggregate(CreateUser) has ended it's Unit of Work.
Thus, seeing your code, my only hunch is this "wild, unused" constructor which might obstruct things.
Lastly, I need to recommend you to use a more recent version of Axon.
3.3.3 is already quite far away from the current release, being 4.2.
Additionally, no active development is taking place on Axon 3.x versions.
It is thus wise to upgrade the version, which I assume shouldn't be a big deal as you are still defining your Command Model.
Update
I've just closed the Framework issue you've opened up. Axon provides entirely different means to tie in to the Message dispatching and handling, giving you cleaner intercept points than (Spring) AOP.
If you following the suggested guidelines to use a MessageDispatchInterceptor/MessageHandlerInterceptor or the more fine grained option with HandlerEnhancer, you can achieve these cross-cutting concerns you are looking for.
As far as logging goes, the framework even provides a LoggingInterceptor to do exactly what you need. No AOP needed.
Hope this helps you out Narasimha.
Thank you #Steven for the response.
I am able to reproduce this issue with Axon 4.2(latest) version also.
After removing the below AOP code in my project, The issue solved automatically.
Looks like Axon is missing compatible with the AOP feature.
AOP Code:
#Around("execution(* com.ms.axonspringboot..*(..))")
public Object methodInvoke(ProceedingJoinPoint jointPoint) throws Throwable {
LOGGER.debug(jointPoint.getSignature() + "::: Enters");
Object obj = jointPoint.proceed();
LOGGER.debug(jointPoint.getSignature() + "::: Exits");
return obj;
}
Axon 4.2 version error logs
2019-10-07 12:52:41.689 WARN 31736 --- [ault-executor-0] o.a.c.gateway.DefaultCommandGateway : Command 'com.ms.axonspringboot.commands.UpdateUserCmd' resulted in org.axonframework.commandhandling.CommandExecutionException(Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest when handling the creation event.)
2019-10-07 12:52:41.710 ERROR 31736 --- [nio-7070-exec-3] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
org.axonframework.axonserver.connector.command.AxonServerRemoteCommandHandlingException: An exception was thrown by the remote message handling component: Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest when handling the creation event.
at org.axonframework.axonserver.connector.ErrorCode.lambda$static$8(ErrorCode.java:84) ~[axon-server-connector-4.2.jar:4.2]
at org.axonframework.axonserver.connector.ErrorCode.convert(ErrorCode.java:180) ~[axon-server-connector-4.2.jar:4.2]
I tried to use these two ways to write a customfield and it is recording correctly, but it is keeping customfields between requests
public class LoggerAudit : ILoggerAudit
{
public void AddOnSavingAction(string key, object value)
{
Configuration.AddOnSavingAction(scope =>
{
scope.SetCustomField(key, value);
//scope.Event.CustomFields.Remove(key);
//scope.Event.CustomFields.Add(key, value);
});
}
}
For example:
In the first request my webapi recorded the customfield 'field-A', but in the second request my webapi, there was no need to write this customfield, but it was kept in scope and consequently in my json
I tried this setting, but it didn't work
.WithAction(action =>
{
action.OnEventSaved(scope => scope.Event.CustomFields = new Dictionary<string, object>());
});
The custom actions attached with AddOnSavingAction / OnEventSaved are globally attached and will execute for each and all the events before saving or after saving occurs (respectively), so you should attach each action just once.
But your use case looks like you don't have a way to derive the custom field value from the audit scope, so a custom action will not be useful.
Also I guess you are using Audit.WebApi extension. If that's the case, you won't need a custom action to add a custom field, since you can access the AuditScope with the provided extension methods directly on your controllers or in any place where you can get the current HttpContext, for example:
using Audit.WebApi;
[AuditApi]
public class UsersController : Controller
{
public IHttpActionResult Get(string id)
{
//...
var auditScope = this.GetCurrentAuditScope();
auditScope.SetCustomField("MyField", Guid.NewGuid());
//...
}
}
or just
private void SetCustomField(HttpContext context, string key, object value)
{
var auditScope = context.GetCurrentAuditScope();
auditScope.SetCustomField(key, value);
}
I have a Spring 3.2 Controller with basic request mappings like
#RequestMapping("/action")
public String action(#RequestParam("param") String param) {
//do stuff...
return "view";
}
This controller handles links created by non-technical business users. Sometimes the users mess it up and create links with duplicate parameters, e.g.,
www.example.com/action?param=value¶m=value
The parameter is an exact duplicate and probably a copy/paste error.
My problem is that Spring is concatenating these dupes together, so that the url above will give "value,value" for param, when I want only "value".
What is a good way to detect and handle these duplicates? I know I could change all my #RequestParams to List<String>s and go from there, but that's a whole lot of boilerplate over dozens of request mappings.
Ideally there would be a way to intercept and modify the url parameters before Spring attempts to bind them -- but only for this controller.
I found that I can register a custom String property editor to do this.
class DuplicateParameterReducingPropertyEditor extends PropertyEditorSupport {
Object value;
#Override
public void setValue(Object value) {
if (value instanceof String[]) {
String[] strings = (String[])value;
Set<String> unique = Sets.newHashSet(strings);
this.value = unique.toArray();
} else {
this.value = value;
}
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
this.value = text;
}
#Override
public String getAsText() {
return value.toString();
}
#Override
public Object getValue() {
return value;
}
};
I added this to my controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
PropertyEditor stringEditor = new DuplicateParameterReducingPropertyEditor();
binder.registerCustomEditor(String.class, stringEditor);
}
So whenever Spring encounters a #RequestParam-annotated String method argument, the PropertyEditor is invoked to transform the incoming data if needed. In the case of duplicate parameters, Spring passes a String[] of the values to the property editor setValue, which I can then manipulate.
This does have the results I am looking for. I'm not sure of all the implications of this, though, so I can't endorse it as good solution yet. Not having to alter any handler method signatures is a big plus though.
A good idea would be to extend AbstractNamedValueMethodArgumentResolver with your own strategy. Then the strategy could be used wherever you deem necessary.
This strategy only works for Spring 3.1+ which is not a problem for you since you are using Spring 3.2
I faced the same issue in Spring boot. Eventually I came up with this solution using converter, in case it helps anyone.
This method should be added as part of your WebMvcConfigurer class.
#Override
public void addFormatters(FormatterRegistry registry) {
// Duplicate query parameters converter
registry.addConverter(new Converter<String[], String>() {
public String convert(String[] arr) {
return arr[arr.length - 1]; // Return the last value
}
});
}
I have a List of objects with nested properties and at the bottom of the hierarchy each object has a CommonsMultipartFile property.
A Folder has a list of Requisites and each of those has a list of Pages
These are the bean definitions, each in its own file:
Page {
private CommonsMultipartFile attributes;
// Getter & Setter
}
Requisite {
private List<Page> pages;
// Other properties and Getters & Setters
}
Folder {
private List<Requisite> requisites;
// Getter & Setter
}
Then I add a Folder object to my modelMap inside a Controller method:
#RequestMapping(value = "loadFiles", method = RequestMethod.GET)
public String initFiles(ModelMap model, HttpServletRequest request) {
Folder folder = new Folder();
folder.setRequisites(requisitesModel.getRequisitesFromDB());
model.addAttribute("folder", folder);
return "loadFiles";
}
At this point the model attribute "folder" has a list of Requisite objects with various properties initialized, however pages (List<Page>) is null in all of them. This approach works fine and allows the user to load a bunch of files and the post request works as expected.
Then I added a method to handle a MaxUploadSizeExceededException and in the resolveException method I copied the behavior of the controller described above. This is to redirect the user to the same form when the total filesize exeeds a given threshold.
Here is the definition of the resolveException method:
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception exception) {
ModelMap model = new ModelMap();
Folder folder = new Folder();
folder.setRequisites(requisitesModel.getRequisitesFromDB());
model.addAttribute("error", "The files exceed the maximum filesize");
model.addAttribute("folder", folder);
return new ModelAndView("loadFiles", model);
}
The problem is that in this one the autoGrowNestedPaths does not work because immediately throws Invalid property 'requisites[0].pages[0]' of bean class [mypackage.Folder]: Cannot access indexed value of property referenced in indexed property path 'pages[0]': returned null.
My understanding is that spring by default autogrows nested paths for all Collection types, even without the use of LazyList or AutopopulatingList. Is my understanding wrong? Do I need to add something else?
I had and solved this problem myself, Jorge.
WebDataBinder does default to auto-grow nested paths; and that includes collections.
But the interesting thing, is that this depends on getting the 'generic type' of the Collection from the property getter method. It uses reflection -- calling Method.getGenericReturnType(), which returns a java.lang.reflect.Type.
If it works, you get a java.lang.reflect.ParameterizedType with the element-type of the collection, to grow; if it doesn't, Spring will get a 'null' element-type & won't auto-grow.
See org.springframework.beans.BeanWrapperImpl.growCollectionIfNecessary().
In my case, Hibernate proxy (subclasses) were found not to have the requisite generic type & method-signature info.. even though this info was on the entity classes (when used directly!)
I de-proxied the entity in my form controller "loadEntity" setup, and was right as rain. (De-proxying is useful & necessary in Hibernate apps sometimes, as are other proxy checks, comparisons, and manipulations.)
Code sample:
public static <T> T deproxy (T obj) {
if (obj == null)
return obj;
if (obj instanceof HibernateProxy) {
// Unwrap Proxy;
// -- loading, if necessary.
HibernateProxy proxy = (HibernateProxy) obj;
LazyInitializer li = proxy.getHibernateLazyInitializer();
return (T) li.getImplementation();
}
return obj;
}
public static boolean isProxy (Object obj) {
if (obj instanceof HibernateProxy)
return true;
return false;
}
public static boolean isSame (Object o1, Object o2) {
if (o1 == o2)
return true;
if (o1 == null || o2 == null)
return false;
Object d1 = deproxy(o1);
Object d2 = deproxy(o2);
if (d1 == d2)
return true;
return false;
}
public static Class getClassWithoutInitializingProxy (Object obj) {
if (obj instanceof HibernateProxy) {
HibernateProxy proxy = (HibernateProxy) obj;
LazyInitializer li = proxy.getHibernateLazyInitializer();
return li.getPersistentClass();
}
// Not a Proxy.
return obj.getClass();
}
Hope this helps guide you to your problem.. give me an upvote even!
From the BeanWrapper javadoc
setAutoGrowNestedPaths
void setAutoGrowNestedPaths(boolean
autoGrowNestedPaths)
Set whether this BeanWrapper should attempt to "auto-grow" a nested
path that contains a null value. If "true", a null path location will
be populated with a default object value and traversed instead of
resulting in a NullValueInNestedPathException. Turning this flag on
also enables auto-growth of collection elements when accessing an
out-of-bounds index.
Default is "false" on a plain BeanWrapper.
So no, BeanWrapperImpl does not autogrow nested path for lists by default (assuming you are using spring 3). Like you mentioned in your post, you can fix this by using an Autopopulating or Lazy list.
You can also use initbinder to exlicitely set autogrowNestedPaths property to true.
Given that every grid, combobox, checkboxlist and in general multi row control supports binding directly to any IEnumerable what is the point of the ObjectDataSource?
Why would one use it as opposed to binding directly to your collection? Particularly if you already have reasonable separation of concerns in your business, presentation and data layers?
I also feel this is an even more relevant question since the introduction of LINQ. I have often found that when binding I would like to perform some further ordering, exclusion and so forth using LINQ and I believe this is not possible when using the ObjectDataSource without creating a specific method for your (potentially single use case)?
So when is it appropriate to use an ObjectDataSource and what are the advantages compared to direct binding to IEnumerable?
First, ObjectDataSource is usually used in ASP.NET WebForms (aspx). ObjectDataSource is located in System.Web.UI.WebControls, as you can see this link on MSDN Library:
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.objectdatasource.aspx
Using ObjectDataSource to bind your data means you bind you'll have datasource as an object, can be in form of DataSet or any other .NET object that implements IEnumerable. Using ObjectDataSource means you have to perform your own Select, Update, Insert and Delete method that usually found in SqlDataSource.
There's this nice walkthrough in MSDN Library: Walkthrough: Data Binding to a Custom Business Object
But binding to a simple IEnumerable without implementing IListSource (like DataTable has) means you won't have nice feature such as data bindings to a complex data control such as GridView. And you'll lose other feature too, because a simple IEnumerable alone can't be bound in two ways to other list control such as ListView and GridView.
To have your data to be bindable two way, your object must also implement INotifyPropertyChanged interface before added into the IListSource as data item.
Samples:
public class Employee : BusinessObjectBase
{
private string _id;
private string _name;
private Decimal parkingId;
public Employee() : this(string.Empty, 0) {}
public Employee(string name) : this(name, 0) {}
public Employee(string name, Decimal parkingId) : base()
{
this._id = System.Guid.NewGuid().ToString();
// Set values
this.Name = name;
this.ParkingID = parkingId;
}
public string ID
{
get { return _id; }
}
const string NAME = "Name";
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
// Raise the PropertyChanged event.
OnPropertyChanged(NAME);
}
}
}
const string PARKING_ID = "Salary";
public Decimal ParkingID
{
get { return parkingId; }
set
{
if (parkingId != value)
{
parkingId = value;
// Raise the PropertyChanged event.
OnPropertyChanged(PARKING_ID);
}
}
}
}
This is the implementation of INotifyPropertyChanged:
public class BusinessObjectBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}
#endregion
}
You can bind to an enum with it if you dont want codebehind.