View Javadoc
1   /*
2    *    Copyright 2009-2023 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.executor.loader;
17  
18  import static org.junit.jupiter.api.Assertions.assertEquals;
19  import static org.junit.jupiter.api.Assertions.assertFalse;
20  import static org.junit.jupiter.api.Assertions.fail;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.ObjectInputStream;
25  import java.io.ObjectOutputStream;
26  import java.io.ObjectStreamException;
27  import java.io.Serializable;
28  import java.util.ArrayList;
29  
30  import org.apache.ibatis.domain.blog.Author;
31  import org.apache.ibatis.domain.blog.Section;
32  import org.apache.ibatis.executor.ExecutorException;
33  import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
34  import org.apache.ibatis.session.Configuration;
35  import org.junit.jupiter.api.Assertions;
36  import org.junit.jupiter.api.Test;
37  
38  public abstract class SerializableProxyTest {
39  
40    protected Author author = new Author(999, "someone", "!@#@!#!@#", "someone@somewhere.com", "blah", Section.NEWS);
41  
42    static ProxyFactory proxyFactory;
43  
44    @Test
45    void shouldKeepGenericTypes() {
46      for (int i = 0; i < 10000; i++) {
47        Author pc = new Author();
48        Author proxy = (Author) proxyFactory.createProxy(pc, new ResultLoaderMap(), new Configuration(),
49            new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
50        proxy.getBio();
51      }
52    }
53  
54    @Test
55    void shouldSerializeAProxyForABeanWithDefaultConstructor() throws Exception {
56      Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(),
57          new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
58      Object proxy2 = deserialize(serialize((Serializable) proxy));
59      assertEquals(author, proxy2);
60    }
61  
62    @Test
63    void shouldSerializeAProxyForABeanWithoutDefaultConstructor() throws Exception {
64      AuthorWithoutDefaultConstructor author = new AuthorWithoutDefaultConstructor(999, "someone", "!@#@!#!@#",
65          "someone@somewhere.com", "blah", Section.NEWS);
66      ArrayList<Class<?>> argTypes = new ArrayList<>();
67      argTypes.add(Integer.class);
68      argTypes.add(String.class);
69      argTypes.add(String.class);
70      argTypes.add(String.class);
71      argTypes.add(String.class);
72      argTypes.add(Section.class);
73      ArrayList<Object> argValues = new ArrayList<>();
74      argValues.add(999);
75      argValues.add("someone");
76      argValues.add("!@#@!#!@#");
77      argValues.add("someone@somewhere.com");
78      argValues.add("blah");
79      argValues.add(Section.NEWS);
80      Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(),
81          new DefaultObjectFactory(), argTypes, argValues);
82      Object proxy2 = deserialize(serialize((Serializable) proxy));
83      assertEquals(author, proxy2);
84    }
85  
86    @Test
87    void shouldSerializeAProxyForABeanWithoutDefaultConstructorAndUnloadedProperties() throws Exception {
88      AuthorWithoutDefaultConstructor author = new AuthorWithoutDefaultConstructor(999, "someone", "!@#@!#!@#",
89          "someone@somewhere.com", "blah", Section.NEWS);
90      ArrayList<Class<?>> argTypes = new ArrayList<>();
91      argTypes.add(Integer.class);
92      argTypes.add(String.class);
93      argTypes.add(String.class);
94      argTypes.add(String.class);
95      argTypes.add(String.class);
96      argTypes.add(Section.class);
97      ArrayList<Object> argValues = new ArrayList<>();
98      argValues.add(999);
99      argValues.add("someone");
100     argValues.add("!@#@!#!@#");
101     argValues.add("someone@somewhere.com");
102     argValues.add("blah");
103     argValues.add(Section.NEWS);
104     ResultLoaderMap loader = new ResultLoaderMap();
105     loader.addLoader("id", null, null);
106     Object proxy = proxyFactory.createProxy(author, loader, new Configuration(), new DefaultObjectFactory(), argTypes,
107         argValues);
108     Object proxy2 = deserialize(serialize((Serializable) proxy));
109     assertEquals(author, proxy2);
110   }
111 
112   @Test
113   void shouldSerizaliceAFullLoadedObjectToOriginalClass() throws Exception {
114     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(),
115         new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
116     Object proxy2 = deserialize(serialize((Serializable) proxy));
117     assertEquals(author.getClass(), proxy2.getClass());
118   }
119 
120   @Test
121   void shouldGenerateWriteReplace() throws Exception {
122     try {
123       author.getClass().getDeclaredMethod("writeReplace");
124       fail("Author should not have a writeReplace method");
125     } catch (NoSuchMethodException e) {
126       // ok
127     }
128     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(),
129         new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
130     proxy.getClass().getDeclaredMethod("writeReplace");
131   }
132 
133   @Test
134   void shouldNotGenerateWriteReplaceItThereIsAlreadyOne() {
135     AuthorWithWriteReplaceMethod beanWithWriteReplace = new AuthorWithWriteReplaceMethod(999, "someone", "!@#@!#!@#",
136         "someone@somewhere.com", "blah", Section.NEWS);
137     try {
138       beanWithWriteReplace.getClass().getDeclaredMethod("writeReplace");
139     } catch (NoSuchMethodException e) {
140       fail("Bean should declare a writeReplace method");
141     }
142     Object proxy = proxyFactory.createProxy(beanWithWriteReplace, new ResultLoaderMap(), new Configuration(),
143         new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
144     Class<?>[] interfaces = proxy.getClass().getInterfaces();
145     boolean ownInterfaceFound = false;
146     for (Class<?> i : interfaces) {
147       if (i.equals(WriteReplaceInterface.class)) {
148         ownInterfaceFound = true;
149         break;
150       }
151     }
152     assertFalse(ownInterfaceFound);
153   }
154 
155   @Test
156   void shouldNotCreateAProxyForAFullyLoadedBean() throws Exception {
157     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(),
158         new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
159     Author author2 = (Author) deserialize(serialize((Serializable) proxy));
160     assertEquals(author.getClass(), author2.getClass());
161   }
162 
163   @Test
164   void shouldNotLetReadUnloadedPropertyAfterSerialization() throws Exception {
165     ResultLoaderMap loader = new ResultLoaderMap();
166     loader.addLoader("id", null, null);
167     Object proxy = proxyFactory.createProxy(author, loader, new Configuration(), new DefaultObjectFactory(),
168         new ArrayList<>(), new ArrayList<>());
169     Author author2 = (Author) deserialize(serialize((Serializable) proxy));
170     Assertions.assertThrows(ExecutorException.class, author2::getId);
171   }
172 
173   @Test
174   void shouldNotLetReadUnloadedPropertyAfterTwoSerializations() throws Exception {
175     ResultLoaderMap loader = new ResultLoaderMap();
176     loader.addLoader("id", null, null);
177     Object proxy = proxyFactory.createProxy(author, loader, new Configuration(), new DefaultObjectFactory(),
178         new ArrayList<>(), new ArrayList<>());
179     Author author2 = (Author) deserialize(serialize(deserialize(serialize((Serializable) proxy))));
180     Assertions.assertThrows(ExecutorException.class, author2::getId);
181   }
182 
183   @Test
184   void shouldLetReadALoadedPropertyAfterSerialization() throws Exception {
185     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(),
186         new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
187     byte[] ser = serialize((Serializable) proxy);
188     Author author2 = (Author) deserialize(ser);
189     assertEquals(999, author2.getId());
190   }
191 
192   byte[] serialize(Serializable value) throws Exception {
193     try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
194         ObjectOutputStream oos = new ObjectOutputStream(bos)) {
195       oos.writeObject(value);
196       oos.flush();
197       return bos.toByteArray();
198     }
199   }
200 
201   Serializable deserialize(byte[] value) throws Exception {
202     try (ByteArrayInputStream bis = new ByteArrayInputStream(value);
203         ObjectInputStream ois = new ObjectInputStream(bis)) {
204       return (Serializable) ois.readObject();
205     }
206   }
207 
208   public static class AuthorWithWriteReplaceMethod extends Author {
209 
210     public AuthorWithWriteReplaceMethod() {
211     }
212 
213     AuthorWithWriteReplaceMethod(Integer id, String username, String password, String email, String bio,
214         Section section) {
215       super(id, username, password, email, bio, section);
216     }
217 
218     Object writeReplace() throws ObjectStreamException {
219       return this;
220     }
221   }
222 
223   public static class AuthorWithoutDefaultConstructor extends Author {
224 
225     AuthorWithoutDefaultConstructor(Integer id, String username, String password, String email, String bio,
226         Section section) {
227       super(id, username, password, email, bio, section);
228     }
229 
230     protected Object writeReplace() throws ObjectStreamException {
231       return this;
232     }
233   }
234 
235 }