1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.gcloud.memcached.session;
20
21
22 import java.io.ByteArrayInputStream;
23 import java.io.IOException;
24 import java.io.ObjectInputStream;
25 import java.io.Serializable;
26 import java.util.Map;
27
28 import org.eclipse.jetty.gcloud.session.GCloudSessionManager;
29 import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
30 import org.eclipse.jetty.util.StringUtil;
31 import org.eclipse.jetty.util.log.Log;
32 import org.eclipse.jetty.util.log.Logger;
33
34 import com.google.cloud.datastore.Key;
35
36 import net.rubyeye.xmemcached.MemcachedClient;
37 import net.rubyeye.xmemcached.XMemcachedClient;
38 import net.rubyeye.xmemcached.XMemcachedClientBuilder;
39 import net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
40
41
42
43
44
45
46
47 public class GCloudMemcachedSessionManager extends GCloudSessionManager
48 {
49 private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
50
51 protected String _host;
52 protected String _port;
53 protected MemcachedClient _client;
54 protected int _expirySec = 0;
55 private boolean _heartbeats = true;
56
57
58
59
60
61
62
63
64
65
66
67 public class ContextClassloaderSerializingTranscoder extends SerializingTranscoder
68 {
69
70 @Override
71 protected Object deserialize(byte[] in)
72 {
73
74 if (in == null)
75 return null;
76
77 Object rv = null;
78 try (ByteArrayInputStream bis = new ByteArrayInputStream(in);ObjectInputStream is = new ClassLoadingObjectInputStream(bis);)
79 {
80
81 rv = is.readObject();
82 }
83 catch (IOException e)
84 {
85 LOG.warn("Caught IOException decoding " + in.length + " bytes of data", e);
86 }
87 catch (ClassNotFoundException e)
88 {
89 LOG.warn("Caught CNFE decoding " + in.length + " bytes of data", e);
90 }
91
92 return rv;
93
94 }
95 }
96
97
98
99
100
101
102
103
104 public class MemcacheSession extends GCloudSessionManager.Session
105 {
106
107 public MemcacheSession(String sessionId, long created, long accessed, long maxInterval)
108 {
109 super(sessionId, created, accessed, maxInterval);
110 }
111 }
112
113
114
115
116
117
118
119
120 public class SerializableSessionData implements Serializable
121 {
122
123
124
125 private static final long serialVersionUID = -7779120106058533486L;
126 String clusterId;
127 String contextPath;
128 String vhost;
129 long accessed;
130 long lastAccessed;
131 long createTime;
132 long cookieSetTime;
133 String lastNode;
134 long expiry;
135 long maxInactive;
136 Map<String, Object> attributes;
137
138
139
140 public SerializableSessionData()
141 {}
142
143
144 public SerializableSessionData(Session s)
145 {
146 clusterId = s.getClusterId();
147 contextPath = s.getContextPath();
148 vhost = s.getVHost();
149 accessed = s.getAccessed();
150 lastAccessed = s.getLastAccessedTime();
151 createTime = s.getCreationTime();
152 cookieSetTime = s.getCookieSetTime();
153 lastNode = s.getLastNode();
154 expiry = s.getExpiry();
155 maxInactive = s.getMaxInactiveInterval();
156 attributes = s.getAttributeMap();
157 }
158
159
160 private void writeObject(java.io.ObjectOutputStream out) throws IOException
161 {
162 out.writeUTF(clusterId);
163 out.writeUTF(contextPath);
164 out.writeUTF(vhost);
165
166 out.writeLong(accessed);
167 out.writeLong(lastAccessed);
168 out.writeLong(createTime);
169 out.writeLong(cookieSetTime);
170 out.writeUTF(lastNode);
171
172 out.writeLong(expiry);
173 out.writeLong(maxInactive);
174 out.writeObject(attributes);
175 }
176
177 private void readObject(java.io.ObjectInputStream ois) throws IOException, ClassNotFoundException
178 {
179 clusterId = ois.readUTF();
180 contextPath = ois.readUTF();
181 vhost = ois.readUTF();
182 accessed = ois.readLong();
183 lastAccessed = ois.readLong();
184 createTime = ois.readLong();
185 cookieSetTime = ois.readLong();
186 lastNode = ois.readUTF();
187 expiry = ois.readLong();
188 maxInactive = ois.readLong();
189 Object o = ois.readObject();
190 attributes = ((Map<String,Object>)o);
191 }
192 }
193
194
195
196
197
198
199
200
201
202 public int getExpirySec()
203 {
204 return _expirySec;
205 }
206
207
208
209
210 public void setExpirySec(int expirySec)
211 {
212 _expirySec = expirySec;
213 }
214
215
216
217
218
219 public void setHeartbeats (boolean heartbeats)
220 {
221 _heartbeats = heartbeats;
222 }
223
224
225 @Override
226 public void doStart() throws Exception
227 {
228 if (StringUtil.isBlank(_host) || StringUtil.isBlank(_port))
229 throw new IllegalStateException("Memcached host and/or port not configured");
230
231 LOG.info("Memcached host {} port {}", _host, _port);
232
233 XMemcachedClientBuilder builder = new XMemcachedClientBuilder(_host+":"+_port);
234 _client = builder.build();
235 _client.setEnableHeartBeat(_heartbeats);
236
237
238 _client.setTranscoder(new ContextClassloaderSerializingTranscoder());
239 super.doStart();
240 }
241
242 @Override
243 public void doStop() throws Exception
244 {
245 super.doStop();
246 _client.shutdown();
247 _client = null;
248 }
249
250 @Override
251 protected Session load(Key key) throws Exception
252 {
253
254 if (LOG.isDebugEnabled()) LOG.debug("Loading key {} from memcached ", key.name());
255 Session session = loadFromMemcached(key.name());
256 if (session != null)
257 return session;
258
259
260 return super.load(key);
261 }
262
263
264
265
266
267
268 protected Session loadFromMemcached(String key) throws Exception
269 {
270 SerializableSessionData sd = _client.get(key);
271
272 if (sd == null)
273 return null;
274
275 Session session = new MemcacheSession (sd.clusterId, sd.createTime, sd.accessed, sd.maxInactive);
276 session.setLastNode(sd.lastNode);
277 session.setContextPath(sd.contextPath);
278 session.setVHost(sd.vhost);
279 session.setCookieSetTime(sd.cookieSetTime);
280 session.setLastAccessedTime(sd.lastAccessed);
281 session.setLastNode(sd.lastNode);
282 session.setExpiry(sd.expiry);
283 session.addAttributes(sd.attributes);
284 return session;
285 }
286
287
288 @Override
289 protected void save(Session session) throws Exception
290 {
291
292 super.save(session);
293 saveToMemcached(session);
294 }
295
296
297
298 @Override
299 protected void delete (GCloudSessionManager.Session session)
300 {
301 Exception memcacheException = null;
302 try
303 {
304 deleteFromMemcached(session);
305 }
306 catch (Exception e)
307 {
308 memcacheException = e;
309 }
310
311 super.delete(session);
312 if (memcacheException != null)
313 throw new RuntimeException(memcacheException);
314 }
315
316
317 protected void deleteFromMemcached(Session session) throws Exception
318 {
319 Key gcloudKey = makeKey(session, _context);
320 _client.delete(gcloudKey.name());
321 }
322
323
324
325
326
327
328 protected void saveToMemcached(Session session) throws Exception
329 {
330 Key gcloudKey = makeKey(session, _context);
331 _client.set(gcloudKey.name(), getExpirySec(), new SerializableSessionData(session));
332 }
333
334
335
336
337 public String getHost()
338 {
339 return _host;
340 }
341
342
343
344
345 public void setHost(String host)
346 {
347 _host = host;
348 }
349
350
351
352
353 public String getPort()
354 {
355 return _port;
356 }
357
358
359
360
361
362
363 public void setPort(String port)
364 {
365 _port = port;
366 }
367 }