If the Business Data API use internally a type for deserialization, the type must be passed to the API.
For example, find and search requires a type:
ivy.repo.find("myId", foo.bar.Customer.class);
ivy.repo.search(foo.bar.Customer.class)
Basically, all further type information is determined by this type. This should work for most cases, without class loading conflicts, since a Business Data entry is not related to a specific PMV, as we know from the process data.
Internally, no information about the type is serialized to a field when the type of the values of the fields matches the defined type of the field. So all needed type information can be determined by the given type.
Example:
The class Customer has the following fields defined:
- name: String
- age: Number
- address: Address
The Business Data is stored and loaded by the following script:
in.customer.name = “Chuck Norris”;
in.customer.age = 25;
// type of field customer is Address, so ..
in.customer = new Address(); // .. the value matches the type of the field
in.customer.address.street = “Baarerstrasse 123”
// store the customer and get the id
Number id = ivy.repo.getId(ivy.repo.save(in.customer));
// load the customer again by it's id
ivy.repo.find(in, foo.bar.Customer.class);
If we only have one project and all used classes are defined in the same project, the above example will work successfully, till the end of the world..
Extended Use Cases:
What happen, when a class has new or removed fields (compared to the class, the Business Data entry was stored)?
That's simple. When loading a business data entry they are ignored, when the business data gets saved only known fields are stored. This could lead to data loss. E.g. when two PMVs have not the same version of a data class and both save the same business data, they will overwrite each other’s data.
What happen, when an instance of a field has not the same type as the field is defined in the class?
Example:
We extend the class Address
Public class MyCustomAddress extends Address {…}
And use them in our example script:
In.customer.name = “Chuck Norris”;
In.customer.age = 25;
In.customer = new MyCustomAddress(); // instance is NOT of same type as the field address
In.customer.address.street = “Baarerstrasse 123”
Number id = Ivy.repo.getId(Ivy.repo.save(in.customer));
Ivy.repo.find(in, foo.bar.Customer.class);
The Business Data API tries to handle it. The serializer will store the fullname of the class as String to the serialized-data. On deserialization, the class gets resolved by its fullname. For this, the class loader of the current executing Task is used. This allows, that the class MyCustomAddress can be defined in a higher project than the Customer class.
All this is handled internally by the Jackson deserializer. We just set the class loader of the case project as context class loader on the thread, before calling the Jackson deserialization.
Currently there is no API to set a Classloader as Context Class Loader, since it wasn’t used yet.
However, this means too, that a project (like a Portal) which shows a list of Business Data of other projects, has to be the most upper project in the dependency hierarchie, so that the Business Process API is able to find used types – or they are at least in a shared library. The use of interfaces, inheritance should be limited to special cases, so that problems do not occur at all. Instead, try to use composition whenever possible…