View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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.lang.reflect.Method;
29  import java.util.ArrayList;
30  
31  import org.apache.ibatis.domain.blog.Author;
32  import org.apache.ibatis.domain.blog.Section;
33  import org.apache.ibatis.executor.ExecutorException;
34  import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
35  import org.apache.ibatis.session.Configuration;
36  import org.junit.jupiter.api.Assertions;
37  import org.junit.jupiter.api.Test;
38  
39  public abstract class SerializableProxyTest {
40  
41    protected Author author = new Author(999, "someone", "!@#@!#!@#", "someone@somewhere.com", "blah", Section.NEWS);
42  
43    static ProxyFactory proxyFactory;
44  
45    @Test
46    void shouldKeepGenericTypes() {
47      for (int i = 0; i < 10000; i++) {
48        Author pc = new Author();
49        Author proxy = (Author) proxyFactory.createProxy(pc, new ResultLoaderMap(), new Configuration(), new DefaultObjectFactory(),
50            new ArrayList<>(), new ArrayList<>());
51        proxy.getBio();
52      }
53    }
54  
55    @Test
56    void shouldSerializeAProxyForABeanWithDefaultConstructor() throws Exception {
57      Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(), 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", "!@#@!#!@#", "someone@somewhere.com", "blah", Section.NEWS);
65      ArrayList<Class<?>> argTypes = new ArrayList<>();
66      argTypes.add(Integer.class);
67      argTypes.add(String.class);
68      argTypes.add(String.class);
69      argTypes.add(String.class);
70      argTypes.add(String.class);
71      argTypes.add(Section.class);
72      ArrayList<Object> argValues = new ArrayList<>();
73      argValues.add(999);
74      argValues.add("someone");
75      argValues.add("!@#@!#!@#");
76      argValues.add("someone@somewhere.com");
77      argValues.add("blah");
78      argValues.add(Section.NEWS);
79      Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(), new DefaultObjectFactory(), argTypes, argValues);
80      Object proxy2 = deserialize(serialize((Serializable) proxy));
81      assertEquals(author, proxy2);
82    }
83  
84    @Test
85    void shouldSerializeAProxyForABeanWithoutDefaultConstructorAndUnloadedProperties() throws Exception {
86      AuthorWithoutDefaultConstructor author = new AuthorWithoutDefaultConstructor(999, "someone", "!@#@!#!@#", "someone@somewhere.com", "blah", Section.NEWS);
87      ArrayList<Class<?>> argTypes = new ArrayList<>();
88      argTypes.add(Integer.class);
89      argTypes.add(String.class);
90      argTypes.add(String.class);
91      argTypes.add(String.class);
92      argTypes.add(String.class);
93      argTypes.add(Section.class);
94      ArrayList<Object> argValues = new ArrayList<>();
95      argValues.add(999);
96      argValues.add("someone");
97      argValues.add("!@#@!#!@#");
98      argValues.add("someone@somewhere.com");
99      argValues.add("blah");
100     argValues.add(Section.NEWS);
101     ResultLoaderMap loader = new ResultLoaderMap();
102     loader.addLoader("id", null, null);
103     Object proxy = proxyFactory.createProxy(author, loader, new Configuration(), new DefaultObjectFactory(), argTypes, argValues);
104     Object proxy2 = deserialize(serialize((Serializable) proxy));
105     assertEquals(author, proxy2);
106   }
107 
108   @Test
109   void shouldSerizaliceAFullLoadedObjectToOriginalClass() throws Exception {
110     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(), new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
111     Object proxy2 = deserialize(serialize((Serializable) proxy));
112     assertEquals(author.getClass(), proxy2.getClass());
113   }
114 
115   @Test
116   void shouldGenerateWriteReplace() throws Exception {
117     try {
118       author.getClass().getDeclaredMethod("writeReplace");
119       fail("Author should not have a writeReplace method");
120     } catch (NoSuchMethodException e) {
121       // ok
122     }
123     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(), new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
124     Method m = proxy.getClass().getDeclaredMethod("writeReplace");
125   }
126 
127   @Test
128   void shouldNotGenerateWriteReplaceItThereIsAlreadyOne() {
129     AuthorWithWriteReplaceMethod beanWithWriteReplace = new AuthorWithWriteReplaceMethod(999, "someone", "!@#@!#!@#", "someone@somewhere.com", "blah", Section.NEWS);
130     try {
131       beanWithWriteReplace.getClass().getDeclaredMethod("writeReplace");
132     } catch (NoSuchMethodException e) {
133       fail("Bean should declare a writeReplace method");
134     }
135     Object proxy = proxyFactory.createProxy(beanWithWriteReplace, new ResultLoaderMap(), new Configuration(), new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
136     Class<?>[] interfaces = proxy.getClass().getInterfaces();
137     boolean ownInterfaceFound = false;
138     for (Class<?> i : interfaces) {
139       if (i.equals(WriteReplaceInterface.class)) {
140         ownInterfaceFound = true;
141         break;
142       }
143     }
144     assertFalse(ownInterfaceFound);
145   }
146 
147   @Test
148   void shouldNotCreateAProxyForAFullyLoadedBean() throws Exception {
149     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(), new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
150     Author author2 = (Author) deserialize(serialize((Serializable) proxy));
151     assertEquals(author.getClass(), author2.getClass());
152   }
153 
154   @Test
155   void shouldNotLetReadUnloadedPropertyAfterSerialization() throws Exception {
156     ResultLoaderMap loader = new ResultLoaderMap();
157     loader.addLoader("id", null, null);
158     Object proxy = proxyFactory.createProxy(author, loader, new Configuration(), new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
159     Author author2 = (Author) deserialize(serialize((Serializable) proxy));
160     Assertions.assertThrows(ExecutorException.class, author2::getId);
161   }
162 
163   @Test
164   void shouldNotLetReadUnloadedPropertyAfterTwoSerializations() throws Exception {
165     ResultLoaderMap loader = new ResultLoaderMap();
166     loader.addLoader("id", null, null);
167     Object proxy = proxyFactory.createProxy(author, loader, new Configuration(), new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
168     Author author2 = (Author) deserialize(serialize(deserialize(serialize((Serializable) proxy))));
169     Assertions.assertThrows(ExecutorException.class, author2::getId);
170   }
171 
172   @Test
173   void shouldLetReadALoadedPropertyAfterSerialization() throws Exception {
174     Object proxy = proxyFactory.createProxy(author, new ResultLoaderMap(), new Configuration(), new DefaultObjectFactory(), new ArrayList<>(), new ArrayList<>());
175     byte[] ser = serialize((Serializable) proxy);
176     Author author2 = (Author) deserialize(ser);
177     assertEquals(999, author2.getId());
178   }
179 
180   byte[] serialize(Serializable value) throws Exception {
181     try(ByteArrayOutputStream bos = new ByteArrayOutputStream();
182         ObjectOutputStream oos = new ObjectOutputStream(bos)) {
183       oos.writeObject(value);
184       oos.flush();
185       return bos.toByteArray();
186     }
187   }
188 
189   Serializable deserialize(byte[] value) throws Exception {
190     try(ByteArrayInputStream bis = new ByteArrayInputStream(value);
191     ObjectInputStream ois = new ObjectInputStream(bis)) {
192       return (Serializable) ois.readObject();
193     }
194   }
195 
196   public static class AuthorWithWriteReplaceMethod extends Author {
197 
198     public AuthorWithWriteReplaceMethod() {
199     }
200 
201     AuthorWithWriteReplaceMethod(Integer id, String username, String password, String email, String bio, Section section) {
202         super(id, username, password, email, bio, section);
203     }
204 
205     Object writeReplace() throws ObjectStreamException {
206       return this;
207     }
208   }
209 
210   public static class AuthorWithoutDefaultConstructor extends Author {
211 
212     AuthorWithoutDefaultConstructor(Integer id, String username, String password, String email, String bio, Section section) {
213         super(id, username, password, email, bio, section);
214     }
215 
216     protected Object writeReplace() throws ObjectStreamException {
217       return this;
218     }
219   }
220 
221 }