I'm currently migrating an JavaEE5 (WL 10.3.5) application to JavaEE 7 (WL 12.2.1) and I'm facing a strange issue.
The WebLogic 12c implemntation of method HttpSession.setAttribute(String, Object) does not replace the old object bound to the same string key by a new object if the objects are identical (identical according WL12c's implemntation).
After several tests, when you want to replace an object in session, WL12c compare hashCode if they are the same, it compare object whith equals(). If equals() return 'true' WL12c does NOT replace the object!
But there is no such information in the JavaEE 7 Api doc : setAttribute(String, Object)
Binds an object to this session, using the name specified. If an
object of the same name is already bound to the session, the object is
replaced.
No mention at all of " only if the object to replace is not identical according the hashCode and equals methods".
Example to test, suppose we have a class MyClass
import org.apache.commons.lang.builder.HashCodeBuilder;
class MyClass{
private Long id;
private String myProperty;
private static final int HASHCODE1 = -459751453;
private static final int HASHCODE2 = 981454267;
public MyClass(Long id, String myProperty) {
this.id = id;
this.myProperty = myProperty;
}
// Override equals based on the id only
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
MyClass myClass = (MyClass) obj;
if (this.getId() != null) {
return this.getId().equals(myClass.getId());
}
return false;
}
// Override hashCode based only on id
#Override
public int hashCode() {
return new HashCodeBuilder(HASHCODE1, HASHCODE2).append(this.getId()).toHashCode();
}
/* Getters & Setters*/
}
And in a servlet we do
MyClass obj1 = new MyClass(1L, "prop1");
MyClass obj2 = new MyClass(1L, "prop2");
request.getSession().removeAttribute("key");
request.getSession().setAttribute("key", obj1);
request.getSession().setAttribute("key", obj2);
Two object with same id. In WL12c, the session will still contain obj1.
If I change the code (obj1.id = 2)
MyClass obj1 = new MyClass(1L, "prop1");
MyClass obj2 = new MyClass(2L, "prop2");
request.getSession().removeAttribute("key");
request.getSession().setAttribute("key", obj1);
request.getSession().setAttribute("key", obj2);
The session will contain obj2.
We didn't have this behaviour in WebLogic 10.3.5 which have the same doc. So, does WL12c not complying the Java EE 7 API ? If not why ?
Rem : by extension I think it's the same for the request and application attributes.
Edit 1: I tested this simple webapp (same war) in Tomcat 9 and in Wildfly 11. WebLogic 12.2.1 is the only one which don't replace the object in session.
Edit 2: I did not indicate it in my original post but I only found one topic about this problem and it was closed without any relevant answer:
HttpSession setAttribute doesn't always insert new object
Session management in Weblogic 12c got many updates. visit your Weblogic administration page to see how your session is being manged. JDBC-based session persistence may be the reasion.
Related
I have project that need to reference to some web service, just say my reference is
service1Facade and service2Facade
both of them contain class name objectA
i must load objectA from service1Facade and use it as parameter in service2Facade.
but i got error
"value of type service1Facade.objectA cannot be converted to service2Facade.objectA"
how can i convert these object ?
what i have try but still not work:
group all reference into same folder, but .NET change its name into
objectA and objectA1
I copy every property of the property inside objectA, but still not working.
The functionality that is responsible for generating proxy classes based on your WSDL specification doesn't know (and it shouldn't know) that both your services use the same underlying type for objectA, and as I mentioned, no assumptions can be made regarding this since web services are meant to be decoupled from each other (from the consumer point of view).
I'd say your best option is to have your own proxy class (let's say ServiceProxyDTO) that can be used in both service #1 and #2. Something along the lines of:
public class ServiceProxyDTO
{
// Define properties from "objectA"
public ServiceProxyDTO() { }
public ServiceProxyDTO(service1Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public ServiceProxyDTO(service2Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public static implicit operator service1Facade.ObjectA(ServiceProxyDTO dto)
{
return new service1Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator service2Facade.ObjectA(ServiceProxyDTO dto)
{
return new service2Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator ServiceProxyDTO(service1Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
public static implicit operator ServiceProxyDTO(service2Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
}
With this code you can instantiate ServiceProxyDTO and pass it as parameter to both service #1 and #2 (as well as get the return values from both of these services).
Hope this helps.
I created an Array of Object from the .Net environment as follows :
Dim names(2) As User
names(0) = New User("param1", "param2", "param3")
names(1) = New User("param1", "param2", "param3")
Here I have created a User class with 3 String variables. I also created a User class at Java environment with same String variables and the Java class accepts User[] user. I generated webservice based on the Java class and forwrading the parameter from the .Net environment as follows :
Dim MyService As localhost.ReadObject = New localhost.ReadObject
Dim resultString As String = MyService.ReadParameters(names)
i have done what you need. if you pass array object(list is not supported in WebService) then in java you need to use class which has an array type of variable then generate getter and setter of that array type of variable and then generate wsdl again.
if you do that then java service will accepts your array input.
suppose your service need to send "PurchaseOrder" kind of array object then say :
class OuterObject{
public PurchaseOrder[] order;
public PurchaseOrder[] getOrder() {
return order;
}
public PurchaseOrder[] setOrder( PurchaseOrder[] order) {
this.order = order;
}
}
then use OuterObject as input parameter in your service method.say:
class ServiceClass{
public placeOrder(OuterObject object){
PurchaseOrder[] data = object.getOrder();
int ordernumber = data.getOrderNumber();
System.out.println("ordernumber is = " + ordernumber);
}
}
it will work but use only array not list, service doesn't support list.
I have a mapped Entry ("entity") using Spring LDAP ODM. When I run unit tests with this class, I get a warning in the console upon initialization:
Mar 9, 2012 2:32:40 PM org.springframework.ldap.odm.core.impl.ObjectMetaData <init>
WARNING: The Entry class Superhero should be declared final
The mapped class looks like this:
#Entry(objectClasses = {"batman", "superman", "spiderman", "dontworryaboutthese"})
public class Superhero {
#Id
#JsonIgnore
private Name dn;
...
I can't find anything relevant via Google search regarding this warning. Here's the Spring code that logs it:
public ObjectMetaData(Class<?> clazz) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Extracting metadata from %1$s", clazz));
}
// Get object class metadata - the #Entity annotation
Entry entity = (Entry)clazz.getAnnotation(Entry.class);
if (entity != null) {
// Default objectclass name to the class name unless it's specified
// in #Entity(name={objectclass1, objectclass2});
String[] localObjectClasses = entity.objectClasses();
if (localObjectClasses != null && localObjectClasses.length > 0 && localObjectClasses[0].length() > 0) {
for (String localObjectClass:localObjectClasses) {
objectClasses.add(new CaseIgnoreString(localObjectClass));
}
} else {
objectClasses.add(new CaseIgnoreString(clazz.getSimpleName()));
}
} else {
throw new MetaDataException(String.format("Class %1$s must have a class level %2$s annotation", clazz,
Entry.class));
}
// Check the class is final
if (!Modifier.isFinal(clazz.getModifiers())) {
LOG.warn(String.format("The Entry class %1$s should be declared final", clazz.getSimpleName()));
}
...
Any insight would be appreciated. I understand that declaring a class as final means it can't be extended, but why would Spring ODM care?
Security reason ?
Maybe by subclassing your entity, one could store other kind of LDAP entries in the directory, leading to unforseen behavior ?
I have the following:
An interface I1 extends Ia, Ib, Ic
An interface I2.
A class C implements I1, I2. And this class has its own setters and getters as well.
C cInstance = new C():
//Jackson
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(new File("somefile.json"), cInstance);
//Gson
Gson gson = new Gson();
String json = gson.toJson(cInstance);
The output will be cInstance serialized according to the properties of C and what it inherited.
However, I like the properties are being serialized to be according to the setters/getters in I1 (only the cInstance properties represented in the I1 interface).
How can I do this with Jackson knowing that I have too many classes with the same problem and I can't modify the class definition or add annotations.
And the same issue applies to Deserialization (Deserializing according to an interface)
Thanks
First of all, you can always attach "mix-in annotations" even without adding annotations directly (see wiki page). With this, annotation to use would be:
#JsonSerialize(as=MyInterface.class)
but if you do not want to use mix-ins, you can force specific type to use with
objectMapper.typedWriter(MyInterface.class).writeValue(....)
Jackson's VisibilityChecker provides an easy way for filtering certain properties, especially because it allows you to test for visibility (equals "will be serialized or not") for each method/field individually.
At least this helps for the serialization phase.
Here is what I did (using Jackson version 1.9.11):
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.introspect.AnnotatedMethod;
import org.codehaus.jackson.map.introspect.VisibilityChecker;
public static class InterfaceVisibilityChecker extends VisibilityChecker.Std {
private final Set<Method> visibleMethods;
public InterfaceVisibilityChecker(Class<?>... clazzes) {
super(JsonAutoDetect.Visibility.PUBLIC_ONLY);
this.visibleMethods = new HashSet<>();
for (Class<?> clz : clazzes) {
this.visibleMethods.addAll(Arrays.asList(clz.getMethods()));
}
}
#Override
public boolean isGetterVisible(Method m) {
return super.isGetterVisible(m) && isVisible(m);
}
#Override
public boolean isGetterVisible(AnnotatedMethod m) {
return isGetterVisible(m.getAnnotated());
}
private boolean isVisible(Method m) {
for (Method visiMthd : visibleMethods) {
if (isOverwriteMethod(m, visiMthd)) return true;
}
return false;
}
private boolean isOverwriteMethod(Method subMethod, Method superMethod) {
// names must be equal
if (! subMethod.getName().equals(superMethod.getName())) return false;
// return types must be assignable
if (! superMethod.getReturnType().isAssignableFrom(subMethod.getReturnType())) return false;
// parameters must be equal
if (! Arrays.equals(subMethod.getParameterTypes(), superMethod.getGenericParameterTypes())) return false;
// classes must be assignable
return superMethod.getDeclaringClass().isAssignableFrom(subMethod.getDeclaringClass());
}
}
The main idea is to use the standard VisibilityChecker and extend it by a check whether the method is declared in one of the given interfaces.
The checker is applied to an ObjectMapper instance using the following snippet:
ObjectMapper om = new ObjectMapper();
om.setVisibilityChecker(new InterfaceVisibilityChecker(
I1.class,
I2.class,
Ia.class,
Ib.class,
Ic.class
));
Some comments on the solution above:
The checker is not complete, methods like isIsGetterVisible or isFieldVisible can be handled in a similar manner if needed.
isOverwriteMethod is not optimized at all, it's checks could be cached.
I have a base class DomainObject for all my business objects I am using with NHibernate. It contains the Id property.
public abstract class DomainObject
{
public virtual int Id { get; private set; }
}
I would like to write an IEqualityComparer to compare my domain objects. If two objects have the same Id and are the same kind of object they should be equal. However when I use GetType() to get the type of the object, it will return the NHibernate proxy type. So this code:
bool IEqualityComparer.Equals(object x, object y)
{
// null checking code skipped here
if(x is DomainObject && y is DomainObject)
{
return ((DomainObject) x).Id == ((DomainObject) y).Id
&& x.GetType() == y.GetType();
}
return x.Equals(y);
}
Doesn't work correctly, because the type of x is Asset but the type of y is AssetProxy21879bba3e9e47edbbdc2a546445c657.
So, how do I get the entity type on an object that may be a NHibernate proxy object? i.e. in the example above Asset instead of AssetProxy21879bba3e9e47edbbdc2a546445c657?
You can get the real type of a proxy with:
NHibernateUtil.GetClass(x);
or you can add a method to DomainObject like:
public virtual Type GetTypeUnproxied()
{
return GetType();
}
Which is really slick and doesn't depend directly on NHibernate.
Alternatively, one can approach the problem by saying you need to get the true object, rather than the proxy, which, if the session is handy, can be done with:
session.PersistenceContext.Unproxy(x);
As mentioned in another answer, if you're trying to implement equals it would be a good idea to check out the Sharp architecture implementation of Equals.
To get real object instead of proxy you can use
session.PersistenceContext.Unproxy(proxyObject)
But I think you should look at Sharp architecture implementation for Equals.
You can implement a backdoor property as described here to get the actual nonproxied instance.
i took a different aproach in a production project. I simply have a global HiLow Id-Generator which generates Id's unique for all types then i can:
// in DomainEntity
public override bool Equals(object obj)
{
var other = obj as DomainEntity;
if (Id == 0) // IsTransient()
return ReferenceEquals(this, other);
else
return (other != null) && (Id == other.Id);
}
// Comparer
bool IEqualityComparer.Equals(object x, object y)
{
return object.Equals(x, y);
}