2017-11-24 19 views
0

필드가 직렬화 된 순서와 관련된 Apache Ignite Serialization/Deserialization에 문제가 있습니다. 난은 Ignite 캐시에 다음과 같이 "B"의 인스턴스를 둘 필요가 :Apache Ignite : 역 직렬화 후 Hashmap에서 객체를 찾을 수 없습니다.

public class A { 
    private final String name; 

    public A(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 
} 


public class B extends A { 

    private Map<B, String> mapOfB; 

    public B(String name) { 
     super(name); 

     mapOfB = new HashMap<>(); 
    } 

    public void addB(B newB, String someString) { 
     mapOfB.put(newB, someString); 
    } 

    public Map<B, String> getMap() { 
     return mapOfB; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if(obj != null && obj instanceof B) { 
      if(this.getName() == null && ((B) obj).getName() == null && this == obj) { 
       return true; 
      } else if(this.getName().equals(((B) obj).getName())) { 
       return true; 
      } 
     } 
     return false; 
    } 

    @Override 
    public int hashCode() { 
     return this.getName()==null? System.identityHashCode(this):this.getName().hashCode(); 
    } 
} 

의 I는 다음 코드를 실행하는 경우 :

public static void main(String[] args) { 
     // write your code here 
     B b1 = new B("first"); 
     b1.addB(b1, "some first string"); 
     B b2 = new B("second"); 
     b1.addB(b2, "some second string"); 

     // init Ignite configuration 
     // force java.util.Hashtable to be binary serialized, 
     // it prevents infinite recursion and other problems 
     // occurring with the Optimized Serializer 
     IgniteConfiguration cfg = new IgniteConfiguration(); 
     BinaryConfiguration binConf = new BinaryConfiguration(); 
     Collection<String> binClassNames = new LinkedList<>(); 
     binClassNames.add("java.util.Hashtable"); 
     binConf.setClassNames(binClassNames); 
     cfg.setBinaryConfiguration(binConf); 
     Ignition.start(cfg); 

     // put b1 in cache 
     IgniteCache cache = Ignition.ignite().getOrCreateCache("MyCache"); 
     cache.put(b1.hashCode(), b1); 

     //get b1 from cache 
     B b1FromCache= (B) cache.get(b1.hashCode()); 

     // print map values 
     System.out.println("b1 map value: " + b1.getMap().get(b1)); 
     System.out.println("b1 from cache map value: " + b1FromCache.getMap().get(b1)); 
    } 

출력

B1지도 값은 다음과 같습니다 일부 첫 번째 문자열 캐시 맵 값에서

B1 : 널 (null)

문제는 자식의 필드가 부모의 필드보다 먼저 직렬화되기 때문에 Ignite가 B를 deserialize 할 때 먼저 빈 B 객체 (null "name"및 "mapOfB")를 생성 한 다음 mapOfB를 deserialize하려고 시도한다는 것입니다. . 그것은 Hashtable을 생성하고 그것을 포함하기 위해 포함하고있는 각 객체를 deserialize합니다.

위의 예제에서 b2의 경우 b2에 대한 참조가 아직 존재하지 않으므로 아무런 문제가 없습니다. 따라서 새로운 b2 객체가 생성되고 b2 필드가 채워집니다 ("name"필드 포함). 올바른 해시를 사용하여 해시 맵에 추가되었습니다.

그러나 b1의 경우 deserialization이 시작되었으므로 개체가 Ignit의 deserialize 된 개체 맵에 이미 있지만 null 이름 (deserialization은 b1에서 진행 중임)과이 null 이름으로 계산 된 hashCode를 사용합니다. Hashtable은 그 시점에서 계산 된 hashCode와 함께 b1을 넣습니다. 따라서 마지막에지도에서 null이 아닌 이름을 가진 b1을 찾으려고하면 찾을 수 없습니다.

A 및 B 클래스와이 오브젝트의 작성 방법 (Hashmap 작성 방법 ...)을 변경할 수 없으므로 직렬화를 변경하여 해결해야합니다. 그렇게 할 수있는 간단한 방법이 있습니까?

비고 : 실제 코드는 실제 B와 Hashmap 사이의 클래스와 함께보다 복잡합니다.

+0

'b1.addB (새 B ("첫 번째"), "일부 첫 번째 문자열")'과 비슷한 것이지만 이것은 매우 불안정하며 'hasCode'와'equals '의 구현 방법으로 만 작동합니다. ..'B'는 불변이 아니므로 맵의 키로 사용해서는 안됩니다. –

+0

감사. 나는 추한 것을 동의하지만이 코드를 변경할 수는 없습니다. 또한 솔루션이 작동하지만 hashMap이 어떻게 채워지는지 제어 할 수 없기 때문에 솔루션을 사용할 수 없다는 데 동의합니다. 이 텍스트를 편집하여 다시 추가하겠습니다. 다시 한 번 감사드립니다. – Nicolas

답변

1

이러한 문제가 발생한 이유는 정확합니다. name 필드 앞에 mapOfB 필드가 deserialize되고 해당 이름에 hashCode()이 종속됩니다. 그리고 B의 필드는지도에 키로 입력하면 바뀌므로 hashCode이 변경됩니다.

데이터 모델을 변경하는 것이 좋습니다.하지만 할 수 없으므로 여기에 또 다른 옵션이 있습니다. OptimizedMarshaller은 간단한 Java 직렬화를 사용하기 때문에 Maps에 문제가없는 것 같습니다. 그러나 BinaryObject 추상화와 다른 몇 가지 기능을 사용할 수는 없습니다. 해제 것으로,

marshaller.setRequireSerializable(false); 

을하지만 참고 : Serializable 인터페이스를 구현하지 않는 값을 저장하는 경우

OptimizedMarshaller marshaller = new OptimizedMarshaller(); 
cfg.setMarshaller(marshaller); 

는, 당신은 적절하게 구성해야 할 수 있습니다 : 여기 는 OptimizedMarshaller을 가능하게 할 수있는 방법입니다 requireSerializable 플래그가 부정적인 방법으로 직렬화 성능에 영향을 줄 수 있습니다.

+0

답변 해 주셔서 감사합니다. 실제로 OptimizedMarshaller를 강제로 수행 할 필요는 없습니다. Hashmap은 Serializable을 구현하기 때문에 기본적으로 OptimizedMarshaller에 포함 된 모든 요소를 ​​사용하여 직렬화됩니다. 그러나 이것은 HashMap이 Hashmap을 포함하고있는 b1을 포함하고있어서 OptimizedMarshaller가 무한 재귀를 막지 못하기 때문에 무한 재귀를 유도합니다. 그래서 여기서는 사용할 수 없습니다. 이를 방지하기 위해 Hashmap에 대한 바이너리 직렬화 (전체 애플리케이션의 다른 많은 java.util 클래스)를 강제해야한다. – Nicolas

+0

클래스 B가 Serializable을 구현하지 않으면 requireSerializable 만 false로 설정하면됩니다. 그렇지 않으면 제대로 작동합니다. 귀하의 예제에 대해 구성된 OptimizedMarshaller를 사용하여 무한 재귀에 문제가 발생하지 않았습니다. BinaryConfiguration을 완전히 제거하면 잘 작동합니다. Ignite의 어떤 버전을 사용합니까? 문제와 예외를 초래하는 재생산기를 제공 할 수 있습니까? 그것은 아마 별도의 질문으로 할 것이 좋습니다. – Denis

+0

당신 말이 맞습니다. 당신의 솔루션이 효과가 있습니다. 이것 대단히 감사합니다! Ignite 2.2.0을 사용합니다. – Nicolas