Casting to and from pdx in an Apache Geode event listener - gemfire

In a linked question I asked about a problem with Geode.Net client casting Geode PDX types to objects (domain objects) and this time I've a problem casting objects to Geode PDX types. It seems a region can hold both pdx serialized object types and other types of object and / or internal object types. By working with byte offsets I guess.
Anyway, this link shows a way to check for object types and handle them:
// get checks Object type and handles each appropriately
Object myObject = myRegion.get(myKey);
either as PDX instances or as domain object types:
if (myObject instanceof PdxInstance) {
// get returned PdxInstance instead of domain object
PdxInstance myPdxInstance = (PdxInstance)myObject;
then with GetField for a PDX Instance you can update just 1 field, very nice...
My problem is with domain objects:
else if (myObject instanceof DomainClass) {
// code to handle domain object instance
When coming from an event listener
public void AfterCreate(EntryEvent<TKey, TVal> ev)
I first of all try to cast ev.NewValue to Pdx or then the domain object and keep getting one of the following:
When the value in the region is a PdxInstance then without the if (myObject instanceof PdxInstance) as above to prevent it the cast IPdxInstance pdx = (IPdxInstance)ev.NewValue gives:
System.InvalidCastException: Unable to cast object of type myObject to type
Apache.Geode.Client.IPdxInstance
Which is to be expected. I seem to have broken the PdxSerialisation of myObject by creating the region <key, value> as <string, Object> even though myObject extends PdxSerializable and writing keys & values to the region is going through the ToData() override.
So then to deal with myObject directly for example myObject = ev.NewValue or myObject.Field1 = ((myObject)(ev.NewValue)).Field1 or like variations is giving:
Cannot implicitly convert type 'TVal' to 'myObject'
Cannot convert type 'TVal' to 'myObject'
Surely the event ev.NewValue to myObject cast ought to be straightforward so what am I missing? Otherwise I have to use ev.Key (which does cast without any exception) to specifically get the value again from the region in the cache by using:
IRegion<string, Object> r = cache.GetRegion<string, Object>(region);
return r[key];
So when the object is already given in the TVal type NewValue then why I just can't access it there? :-s

I recommend that you remove "read-serialized=true" and remove the type checking for IPdxInstance. You can still have PDX objects in your region without read-serialized=true. You really want the deserialized domain object itself in your callback and not the PDX instance. Turning off read-serialized will accomplish that and give you back your deserialized domain object in the AfterCreate method.
Note: The above recommendation is based on the fact that you are using a .NET client and .NET is giving you a compile error. This does not occur with Java clients. It's a bit puzzling why the .NET interaction is doing this.
FYI that it's well known that a region may contain either a PDX instance or a domain object in a region where PDX is being used. This is for performance efficiency. A primary node may store the domain object in a non-PDX form to avoid serialization costs but a PDX version on a secondary server. That is why clients always need to check the type before operating on the instance.
Yes, I understood that string was your key but it was whether or not you were using the Native Client that was my key question. The lower-case "s" was a clue.

Per programming your application to use PDX instances a region can hold either objects or PDX wrapped objects. For the former, casting through object is working. So I needed to write:
object o = ev.NewValue;
if (o.GetType().Name.Equals("PdxInstanceImpl"))
{
PdxInstance myPdx = (PdxInstance)o;
MyObject m = (MyObject)myPdx.GetObject();
string s = m.MyString;
decimal d = m.MyDecimal;
}
else
{
MyObject m = (MyObject)o;
string s = m.MyString;
decimal d = m.MyDecimal;
}

Related

Kotlin lazy var throwing ClassCastException: kotlin.UNINITIALIZED_VALUE

I've reached the hair pulling out stage with this one! I have a lazy var declared in a Kotlin data class. It is a Boolean and calculates it's value based on other fields in the class.
The way that the Lazy.kt implementation works is to creat an anonymous object called UNINITIALIZED_VALUE to use as the basis of determining whether or not the lazy var has been initialized. Each lazy var instance will have its own unique instance of UNINITIALIZED_VALUE. This object is stored in the _value field of the Lazy object.
When the value of the lazy var is accessed a check is made to see whether the current value matches the UNINITIALIZED_VALUE by doing a reference comparison. If not, then the initializer is called to create the value which is the stored in the lazy var _value field.
What appears to be happening in my case is that the UNINITALIZED_VALUE object is being created and assigned to the value during the lazy var creation - so far so good. However, when I attempt to retrieve the value, inexplicably, the UNITIALIZED_VALUE object has somehow been set to a different object value to the one originally stored in the _value field and so the comparison fails, the lazy implementation thinks the value has been set and so attempts to cast the UNINITIALIZED_VALUE object to a Boolean and boom! Exception!
What's really weird is that I am the only person in the team experiencing this issue - I have deleted and reinstalled everything - Android Studio, full git clone, gradle cache, emulators. I can't think of anything else and am unable to see the UNINITIALIZED_VALUE object being set twice with the debugger.

Adding Transient modifier to existing object

I have a Object Defined as
class MyObj {
int a;
String b;
Date d; //I will add the transient
}
During serialization the object was without 'transient' and the serialized string contains the Date. Now I am adding the 'transient' modifier with the Date. Will it be able to deserialize?
I have tested with my machine and it is working but somehow I am not confident on this one. I am using Gson for serialize/deserialize.
No.!
that is exactly the definition of transient keyword in java
Quote
transient is a Java keyword which marks a member variable not to be
serialized when it is persisted to streams of bytes.transient is a
Java keyword which marks a member variable not to be serialized when
it is persisted to streams of bytes.

What is mean by serializing from one VM to another when using JPA

I am reading JPA 2.0. I encounter a sentence that
We have used the transient modifier instead of the #Transient annotation so that
if the Employee gets serialized from one VM to another then the translated name
will get reinitialized to correspond to the locale of the new VM.
#Entity
public class Employee {
#Id private int id;
private String name;
private long salary;
transient private String translatedName;
// ...
public String toString() {
if (translatedName == null) {
translatedName = ResourceBundle.getBundle("EmpResources").getString("Employee");
}
return translatedName + ": " + id + " " + name;
}
}
What I understood is that when we use #Entity annotation and container encounter it then it call JPA provider that do the things. Like map id to ID column in database. Although we didn't mention the #Column annotation on the name and salary, but by default it maps to column NAME and SALARY in database. We used transient on translatedName so the JAP leave it as it is, not mapping applied to it. It's just a field in this class. But i am unable to get the understanding of the sentence
if the Employee gets serialized from one VM to another
Someone please explain it to me? Also tell me that what i defined above about the workflow of JAP is correct? Like what happening when container encounter #Entity annotation?
Thanks
When a class implements the java.io.Serializable interface, instances of this class are serializable. That means that the JVM can transform the object into a sequence of bytes. These bytes can be sent over the network, or saved on a disk, and can be read by another VM and transformed back into a Java object.
If a field has the transient Java keyword, it means that this field will be ignored by this serialization mechanism. The field won't be serialized.
A field annotated with #Transient is considered as a non-persistent field by JPA. It won't save it in the database, and it won't load it from the database. But it will be serialized if the object is sent to another JVM.
The Java transient keyword automatically makes a field #Transient. This means that a transient field, won't be serialized, and won't be saved by JPA either.
In the "JEE5 world" you can use detached entities as you would have used transfer objects. (I am not judging whether this is a good idea or not!)
Thus you can call for example a service method (e.g. EJB 3 SLSB method) that returns an instance of Employee remotely with the usual remote-call semantics regarding serialization.
It should be noted, that if an instance of Employee was serialized successfully, then your Java Runtime might be broken, as the class does not implement Serializable.
If you don't want to save the state of your entity arrtibute to DB and also don't want the state to get transferred to another jvm, then use Transient keyword.
If you don't want to save the state of your entity arrtibute to DB, but want the state to be transferred to another jvm, then use #Transient annotation.

How to transfer objects through the header in WCF

I'm trying to transfer some user information in the header of the message through message inspectors.
I have created a behavior which adds the inspector to the service (both client and server).
But when I try to communicate with the service I get the following error:
XmlException:
Name cannot begin with the '<' character, hexadecimal value 0x3C.
I have also get exception telling me that DataContracts where unexpected.
Type
'System.DelegateSerializationHolder+DelegateEntry'
with data contract name
'DelegateSerializationHolder.DelegateEntry:http://schemas.datacontract.org/2004/07/System'
is not expected. Consider using a
DataContractResolver or add any types
not known statically to the list of
known types - for example, by using
the KnownTypeAttribute attribute or by
adding them to the list of known types
passed to DataContractSerializer.
The thing is that my object contains other objects which are marked as DataContract and I'm not interested adding the KnownType attribute for those types.
Another problem might be that my object to serialize is very restricted in form of internal class and internal properties etc.
Can anyone guide me in the right direction. What I'm I doing wrong?
Some code:
public virtual object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var header = MessageHeader.CreateHeader("<name>", "<namespace>", object);
request.Headers.Add(header);
return Guid.NewGuid();
}
Don't put the angle brackets into the actual strings. Remember, the serialization format may not even be text based, all you're doing is specifying the name of the element and the namespace. So your code should look more like this:
var header = MessageHeader.CreateHeader("name", "urn:myNamespace", object);
request.Headers.Add(header);
To close this question, I never solved the exception. Instead I implementated ISerializable which worked great for me.

Using Zope object unique id ( _p_oid ) to access object itself

Every Zope object has it's own unique id ( _p_oid ).
To convert it into integer value:
from Shared.DC.xml.ppml import u64 as decodeObjectId
oid = decodeObjectId(getattr(<Object instance>, '_p_oid'))
Is it possible to get object itself having it's _p_oid?
I tried this:
from ZODB.utils import p64
object = <RootObject instance>._p_jar[p64(oid)]
But it seems it's a wrong way because after getting object I can't change any property and object.absolute_url() returns empty string.
This should work, as long as the class of the object you're trying to load is available in the Python environment, and as long as your oid isn't from another database mounted somewhere within the root.
Can you describe the way in which this is failing to work for you?
See whether the following works (it should get the root object, which has _p_oid == 0):
>>> object = root_obj._p_jar[p64(0)]
You can access the object just fine that way, but you get an unwrapped object.
In Zope, the object is normally retrieved via traversal, and every next object you retrieve this way is wrapped in the correct acquisition context. This context tells every object what it's parent object is, and this is in turn used to calculate the object's absolute URL and it's security context.
You would be better off using the Zope intid facilities (via it's five.intid integration layer); that gives you a unique integer ID for each object, and the utility not only keeps track of the object but also of it's path, so you can get the object back with the correct context.
As far as I know, the correct way to do it is to use the get method of the connection instance:
>>> db = DB(...)
>>> conn = db.open()
>>> obj = conn.get(oid)
EDIT: it seems that dbroot._p_jar is an ZODB.Connection.Connection object just like the return type of db.open() so perhaps it can be assumed that both ways are equivalent. Arguably, conn.get(...) seems cleaner as it does not involve accessing underscore-prefixed methods.