View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    * <p>
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    * <p>
9    * or (per the licensee's choosing)
10   * <p>
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic;
15  
16  import java.io.ByteArrayInputStream;
17  import java.io.ByteArrayOutputStream;
18  import java.io.FileInputStream;
19  import java.io.IOException;
20  import java.io.ObjectOutputStream;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.junit.jupiter.api.AfterEach;
25  import org.junit.jupiter.api.BeforeEach;
26  import org.junit.jupiter.api.Test;
27  import org.slf4j.LoggerFactory;
28  
29  import ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream;
30  import ch.qos.logback.core.net.HardenedObjectInputStream;
31  import ch.qos.logback.core.testUtil.CoreTestConstants;
32  
33  import static org.junit.jupiter.api.Assertions.assertEquals;
34  import static org.junit.jupiter.api.Assertions.assertTrue;
35  
36  public class LoggerSerializationTest {
37  
38      static final String SERIALIZATION_PREFIX = CoreTestConstants.TEST_INPUT_PREFIX + "/serialization/";
39  
40      // force SLF4J initialization for subsequent Logger readResolve operation
41      org.slf4j.Logger unused = LoggerFactory.getLogger(this.getClass());
42      LoggerContext lc;
43      Logger logger;
44  
45      ByteArrayOutputStream bos;
46      ObjectOutputStream oos;
47      HardenedLoggingEventInputStream hardenedLoggingEventInputStream;
48      List<String> whitelist = new ArrayList<String>();
49  
50      @BeforeEach
51      public void setUp() throws Exception {
52          lc = new LoggerContext();
53          lc.setName("testContext");
54          logger = lc.getLogger(LoggerSerializationTest.class);
55          // create the byte output stream
56          bos = new ByteArrayOutputStream();
57          oos = new ObjectOutputStream(bos);
58          whitelist.add(Foo.class.getName());
59      }
60  
61      @AfterEach
62      public void tearDown() throws Exception {
63          lc = null;
64          logger = null;
65      }
66  
67      @Test
68      public void basicSerialization() throws IOException, ClassNotFoundException {
69          Foo foo = new Foo(logger);
70          foo.doFoo();
71          Foo fooBack = writeAndRead(foo);
72          fooBack.doFoo();
73      }
74  
75      @Test
76      public void deepTreeSerialization() throws IOException {
77          // crate a tree of loggers under "aaaaaaaa"
78          Logger a = lc.getLogger("aaaaaaaa");
79          lc.getLogger("aaaaaaaa.a");
80          lc.getLogger("aaaaaaaa.a.a");
81          lc.getLogger("aaaaaaaa.a.b");
82          lc.getLogger("aaaaaaaa.a.c");
83          lc.getLogger("aaaaaaaa.a.d");
84  
85          lc.getLogger("aaaaaaaa.b");
86          lc.getLogger("aaaaaaaa.b.a");
87          lc.getLogger("aaaaaaaa.b.b");
88          lc.getLogger("aaaaaaaa.b.c");
89          lc.getLogger("aaaaaaaa.b.d");
90  
91          lc.getLogger("aaaaaaaa.c");
92          lc.getLogger("aaaaaaaa.c.a");
93          lc.getLogger("aaaaaaaa.c.b");
94          lc.getLogger("aaaaaaaa.c.c");
95          lc.getLogger("aaaaaaaa.c.d");
96  
97          lc.getLogger("aaaaaaaa.d");
98          lc.getLogger("aaaaaaaa.d.a");
99          lc.getLogger("aaaaaaaa.d.b");
100         lc.getLogger("aaaaaaaa.d.c");
101         lc.getLogger("aaaaaaaa.d.d");
102 
103         Logger b = lc.getLogger("b");
104 
105         writeObject(oos, a);
106         oos.close();
107         int sizeA = bos.size();
108 
109         bos = new ByteArrayOutputStream();
110         oos = new ObjectOutputStream(bos);
111 
112         writeObject(oos, b);
113         oos.close();
114         int sizeB = bos.size();
115 
116         assertTrue(sizeA < 100, "serialized logger should be less than 100 bytes");
117         // logger tree should not influnce serialization
118         assertTrue((sizeA - sizeB) < 10,
119                 "serialized loggers should be nearly the same size a:" + sizeA + ", sizeB:" + sizeB);
120     }
121 
122     private Foo writeAndRead(Foo foo) throws IOException, ClassNotFoundException {
123         writeObject(oos, foo);
124         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
125         hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(bis, whitelist);
126         Foo fooBack = readFooObject(hardenedLoggingEventInputStream);
127         hardenedLoggingEventInputStream.close();
128         return fooBack;
129     }
130 
131     Foo readFooObject(HardenedObjectInputStream inputStream) throws IOException, ClassNotFoundException {
132         return (Foo) readObject(inputStream);
133     }
134 
135     private Object readObject(HardenedObjectInputStream inputStream) throws IOException, ClassNotFoundException {
136         return inputStream.readObject();
137     }
138 
139     private void writeObject(ObjectOutputStream oos, Object o) throws IOException {
140         oos.writeObject(o);
141         oos.flush();
142         oos.close();
143     }
144 
145     @Test
146     public void testCompatibilityWith_v1_0_11() throws IOException, ClassNotFoundException {
147         FileInputStream fis = new FileInputStream(SERIALIZATION_PREFIX + "logger_v1.0.11.ser");
148         HardenedObjectInputStream ois = new HardenedLoggingEventInputStream(fis); // new String[]
149         // {Logger.class.getName(),
150         // LoggerRemoteView.class.getName()});
151         Logger a = (Logger) ois.readObject();
152         ois.close();
153         assertEquals("a", a.getName());
154     }
155 
156     // interestingly enough, logback 1.0.11 and earlier can also read loggers
157     // serialized by 1.0.12.
158     // fields not serialized are set to their default values and since the fields
159     // are not
160     // used, it works out nicely
161     @Test
162     public void testCompatibilityWith_v1_0_12() throws IOException, ClassNotFoundException {
163         FileInputStream fis = new FileInputStream(SERIALIZATION_PREFIX + "logger_v1.0.12.ser");
164         HardenedObjectInputStream ois = new HardenedObjectInputStream(fis, new String[]{Logger.class.getName()});
165         Logger a = (Logger) ois.readObject();
166         ois.close();
167         assertEquals("a", a.getName());
168     }
169 
170 }