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

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.

Related

Casting to and from pdx in an Apache Geode event listener

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;
}

.NET Reflection - Retrieve a Property Without Knowing Its Name

I've been given a collection of WCF services to use which are all the same, save for the way certain properties of the objects returned by the services are named. If I have services A, B, and C, they each return nearly identical objects except that the objects' properties have been changed to reflect the service from whence they came. So, service A returns an object called responseA with a property responseA.AValidation and B returns a similar object with a property called responseB.BValidation.
Now, as much as might like to make changes to the services, I can't. Which means I'm stuck writing the exact same code for each service all to check for various possible error conditions,
If responseA.AErrors.Length > 0
...
If responseB.BErrors.Length > 0
...
and so on
What I'd love is to write one generic class that can use any of these services and has a single method to perform the above checking on any of them However, I'm stumped on how to check the value of a property when the name of that property can be slightly different from class to class.
So, is it possible to return the value of a property that I don't know the name of until runtime?
Here's what I've tried so far
Dim responseType As Type = responseA.GetType()
For Each Prop In responseType.GetProperties()
If Prop.Name.Contains("ErrorInfoTypes") Then
Dim errorTypes()
errorTypes = Prop.GetValue(GetType(Array), Nothing)
If errorTypes.Length > 0 Then
'Deal with the fact there are errors.
End If
Exit For
End If
Next
This complies, but .GetProperties returns no properties. What am I missing?

GPARs async functions and passing references that are being updated by another thread

I am using GPARs asynchronous functions to fire off a process as each line in a file is parsed.
I am seeing some strange behavior that makes me wonder if I have an issue with thread safety.
Let's say I have a current object that is being loaded up with values from the current row in an input spreadsheet, like so:
Uploader {
MyRowObject currentRowObject
}
Once it has all the values from the current row, I fire off an async closure that looks a bit like this:
Closure processCurrentRowObject = { ->
myService.processCurrentRowObject (currentRowObject)
}.asyncFun()
It is defined in the same class, so it has access to the currentRowObject.
While that is off and running, I parse the next row, and start by creating a new object:
MyObject currentObject = new MyObject()
and start loading it up with values.
I assumed that this would be safe, that the asynchronous function would be pointing to the previous object. However, I wonder if because I am letting the closure bind to the reference, if somehow the reference is getting updated in the async function, and I am pulling the object instance out from under it, so to speak - changing it while it's trying to work on the previous instance.
If so, any suggestions for fixing? Or am I safe?
Thanks!
I'm not sure I fully understand your case, however, here's a quick tip.
Since it is always dangerous to share a single mutable object among threads, I'd recommend to completely separate the row objects used for different rows:
final localRowObject = currentRowObject
currentRowObject = null
Closure processCurrentRowObject = { ->
myService.processCurrentRowObject (localRowObject)
}.asyncFun()

Create a "clone" of this object, not point to it

Let's say I got a list called
myFirstList
And then I want to create a copy of that list so I can do some tweaks of my own. So I do this:
mySecondList = myFirstList
mySecondList.doTweaks
But I noticed that the tweaks also affect the myFirstList object! I only want the tweaks to affect the second one...
And afterwards I will want to completely delete mySecondList, so I do mySecondList = Nothing and I'm good, right?
Adam Rackis, I don't like your "Of course it does", because it is not at all obvious.
If you have a string variable that you assign to another string variabe, you do not change them both when making changes to one of them. They do not point to the same physical piece of memory, so why is it obvious that classes do?
Also, the thing is not even consistent. In the following case, you will have all elements in the array pointing at the same object (they all end up with the variable Number set to 10:
SourceObject = New SomeClass
For i = 1 To 10
SourceObject.Number = i
ObjectArray.Add = SourceObject
Next i
BUT, the following will give you 10 different instances:
For i = 1 To 10
SourceObject = New SomeClass
SourceObject.Number = i
ObjectArray.Add = SourceObject
Next i
Apparently the scope of the object makes a difference, so it is not at all obvious what happens.
Here is how you do it:
'copy one object to another via reflection properties
For Each p As System.Reflection.PropertyInfo In originalobject.GetType().GetProperties()
If p.CanRead Then
clone.GetType().GetProperty(p.Name).SetValue(clone, p.GetValue(OriginalObject, Nothing))
End If
Next
in some cases when the clone object got read-only properties you need to check that first.
For Each p As System.Reflection.PropertyInfo In originalobject.GetType().GetProperties()
If p.CanRead AndAlso clone.GetType().GetProperty(p.Name).CanWrite Then
clone.GetType().GetProperty(p.Name).SetValue(clone, p.GetValue(OriginalObject, Nothing))
End If
Next
Since you have not divulged the type of item that you are storing n your list, I assume it's something that's implementing IClonable (Otherwise, if you can, implement IClonable, or figure out a way to clone individual item in the list).
Try something like this
mySecondList = myFirstList.[Select](Function(i) i.Clone()).ToList()
But I noticed that the tweaks also
affect the myFirstList object! I only
want the tweaks to affect the second
one...
Of course it does. Both variables are pointing to the same object in memory. Anything you do to the one, happens to the other.
You're going to need to do either a deep clone, or a shallow one, depending on your requirements. This article should give you a better idea what you need to do
Expanding on Adam Rackies' answer I was able to implement the following code using VB.NET.
My goal was to copy a list of objects that served mainly as data transfer objects (i.e. database data). The first the class dtoNamedClass is defined and ShallowCopy method is added. A new variable named dtoNamedClassCloneVar is created and a LINQ select query is used to copy the object variable dtoNamedClassVar.
I was able to make changes to dtoNamedClassCloneVar without affecting dtoNamedClassVar.
Public Class dtoNamedClass
... Custom dto Property Definitions
Public Function ShallowCopy() As dtoNamedClass
Return DirectCast(Me.MemberwiseClone(), dtoNamedClass)
End Function
End Class
Dim dtoNamedClassVar As List(Of dtoNamedClass) = {get your database data}
Dim dtoNamedClassCloneVar =
(From d In Me.dtoNamedClass
Where {add clause if necessary}
Select d.ShallowCopy()).ToList
Here's an additional approach that some may prefer since System.Reflection can be slow.
You'll need to add the Newtonsoft.Json NuGet package to your solution, then:
Imports Newtonsoft.Json
And given a class type of MyClass, cloning can be as easy as:
Dim original as New MyClass
'populate properties of original...
Dim copy as New MyClass
copy = JsonConvert.DeserializeObject(Of MyClass)(JsonConvert.SerializeObject(original))
So the approach is to first use the JSON converter to serialize the original object, and than take that serialized data and deserialize it - specifying the class type - into the class instance copy.
The JSON converters are extremely powerful and flexible; you can do all sorts of custom property mappings and manipulations if you need something the basic approach above doesn't seem to address.
this works for me:
mySecondList = myFirstList.ToList
clone is the object you are attempting to clone to.
dim clone as new YourObjectType
You declare it like that.

Type casting in VB .NET

I am adding a feature to an existing VB .Net application that involves retrieving data from a .Net web service. The web service returns an array of Locations. A Location is pretty simple, it has 3 properties – an integer and two strings.
So that the rest of my application does not have to be dependent on this web service, I would like to create my own Location type within my application. My thought is I could call a method that returns a generic list of my Location type which internally calls the web service and populates the list I return. That way, if the data source for Locations changes in the future to something other than the web service, I only have to fix the method instead of fixing all the callers.
So I created my own Location that has identical properties as the service Location. But I don’t seem to be able to cast the array of service locations into a generic list of my locations. I also tried casting a single service Location into one of my Locations and that didn’t work either.
So is casting an option or do I have to loop through each service Location and assign each property to a new one of my Locations? Or is there a completely different way to go about this?
By default you will not be able to cast one Location to another. They are completely unrelated types and thus cannot be the subject of casting. You can make it possible to cast though by defining a custom operator for the application version of CType.
' Location for application
Public Class Location
...
Public Shared Operator Widening CType(p1 as Namespace.Of.WebService.Location) As Location
Dim loc = ConvertWebServiceLocationToApplicationLocation
return loc
End Operator
End Class
This allows you to now do a CType operation between a WebService Location the Application Location.
Casting the array though, simply won't be possible. There is no way to define a conversion operator for arrays so they can't use the above trick. But you can write a quick and dirty function for this purpose
Public Shared Function ConvertArray(ByVal arr As Namespace.Of.WebServiec.Location()) As Location()
Dim newArray(arr.Length) As Location
For i as Integer = 0 To arr.Length - 1
newArray(i) = CType(arr(i), Location)
Next
return newArray
End Function
Casting will not work because these are not the same type. Even if two types look exactly the same, you cannot cast from one to the other, unless you define a CType operator which describes how to transform an object from one type into an object from another type.
Even then, you cannot cast a List(of Type1) into a List(Of Type2) directly.
You will have to loop through and create a new object of your class.