-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathBaseSerializingTranscoder.java
More file actions
206 lines (180 loc) · 6.23 KB
/
BaseSerializingTranscoder.java
File metadata and controls
206 lines (180 loc) · 6.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
* arcus-java-client : Arcus Java client
* Copyright 2010-2014 NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.spy.memcached.transcoders;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Proxy;
import net.spy.memcached.compat.SpyObject;
import net.spy.memcached.transcoders.compression.CompressionCodecIF;
import net.spy.memcached.transcoders.compression.GZIPCompressionCodec;
/**
* Base class for any transcoders that may want to work with serialized or
* compressed data.
*/
public abstract class BaseSerializingTranscoder extends SpyObject {
private final int maxSize;
/*
* This specifies which class loader to use for deserialization
* when there are multiple class loaders.
* If this is null, java default classloader will be used.
*/
private final ClassLoader classLoader;
private final CompressionCodecIF compressionCodec;
protected final TranscoderUtils tu;
/**
* Initialize a serializing transcoder with the given maximum data size.
*/
public BaseSerializingTranscoder(int max) {
this(max, null, true);
}
public BaseSerializingTranscoder(int max, ClassLoader cl) {
this(max, cl, true);
}
public BaseSerializingTranscoder(int max, ClassLoader cl, boolean pack) {
this(max, cl, pack, new GZIPCompressionCodec());
}
public BaseSerializingTranscoder(int max, ClassLoader cl, boolean pack,
CompressionCodecIF codec) {
super();
this.maxSize = max;
this.classLoader = cl;
this.compressionCodec = codec;
this.tu = new TranscoderUtils(pack);
}
/**
* Set the compression threshold to the given number of bytes. This
* transcoder will attempt to compress any data being stored that's larger
* than this.
*
* @param threshold the number of bytes
*/
public void setCompressionThreshold(int threshold) {
compressionCodec.setCompressionThreshold(threshold);
}
public String getCharset() {
return tu.getCharset();
}
/**
* Set the character set for string value transcoding (defaults to UTF-8).
*/
public void setCharset(String to) {
tu.setCharset(to);
}
/**
* Get the bytes representing the given serialized object.
*/
protected byte[] serialize(Object o) {
if (o == null) {
throw new NullPointerException("Can't serialize null");
}
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos)) {
os.writeObject(o);
return bos.toByteArray();
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object, cause=" + e.getMessage(), e);
}
}
/**
* Get the object represented by the given serialized bytes.
*/
protected Object deserialize(byte[] in) {
if (in == null) {
return null;
}
try (ByteArrayInputStream bis = new ByteArrayInputStream(in);
ObjectInputStream is = new ClassLoaderObjectInputStream(bis, this.classLoader)) {
return is.readObject();
} catch (IOException e) {
getLogger().warn("Caught IOException decoding %d bytes of data", in.length, e);
} catch (ClassNotFoundException e) {
getLogger().warn("Caught CNFE decoding %d bytes of data", in.length, e);
}
return null;
}
/**
* Compress the given array of bytes.
*
* @param in the data to compress
* @return the compressed data
* @throws NullPointerException if the input is null
*/
protected byte[] compress(byte[] in) {
return compressionCodec.compress(in);
}
/**
* Decompress the given array of bytes.
*
* @param in the compressed byte array, or null
* @return the decompressed byte array, or null if input is null or decompression fails
*/
protected byte[] decompress(byte[] in) {
return compressionCodec.decompress(in);
}
/**
* Check if the data should be compressed based on its length and the compression threshold.
*
* @param data the data to check
* @return true if the data should be compressed, false otherwise
*/
protected boolean isCompressionCandidate(byte[] data) {
return compressionCodec.isCompressionCandidate(data);
}
public int getMaxSize() {
return maxSize;
}
private static final class ClassLoaderObjectInputStream extends ObjectInputStream {
private final ClassLoader classLoader;
private ClassLoaderObjectInputStream(InputStream in,
ClassLoader classLoader) throws IOException {
super(in);
this.classLoader = classLoader;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass classDesc)
throws IOException, ClassNotFoundException {
if (this.classLoader != null) {
try {
return Class.forName(classDesc.getName(), false, this.classLoader);
} catch (ClassNotFoundException e) {
return super.resolveClass(classDesc);
}
} else {
return super.resolveClass(classDesc);
}
}
@Override
@SuppressWarnings("deprecation") // for java 17 and above
protected Class<?> resolveProxyClass(String[] interfaces)
throws IOException, ClassNotFoundException {
if (this.classLoader != null) {
Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length];
for (int i = 0; i < interfaces.length; ++i) {
resolvedInterfaces[i] = Class.forName(interfaces[i], false, this.classLoader);
}
return Proxy.getProxyClass(this.classLoader, resolvedInterfaces);
} else {
return super.resolveProxyClass(interfaces);
}
}
}
}