View Javadoc

1   /*
2    * Copyright 2009-2010 Capgemini
3    * Licensed under the Apache License, Version 2.0 (the "License"); 
4    * you may not use this file except in compliance with the License. 
5    * You may obtain a copy of the License at 
6    * 
7    * http://www.apache.org/licenses/LICENSE-2.0 
8    * 
9    * Unless required by applicable law or agreed to in writing, software 
10   * distributed under the License is distributed on an "AS IS" BASIS, 
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
12   * See the License for the specific language governing permissions and 
13   * limitations under the License. 
14   * 
15   */
16  package net.sourceforge.statelessfilter.backend.memcache;
17  
18  import java.io.IOException;
19  import java.net.InetSocketAddress;
20  import java.security.SignatureException;
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Map;
24  
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  
28  import net.sourceforge.statelessfilter.backend.ISessionData;
29  import net.sourceforge.statelessfilter.backend.support.CookieBackendSupport;
30  import net.sourceforge.statelessfilter.backend.support.CookieDataSupport;
31  import net.spy.memcached.MemcachedClient;
32  
33  import org.apache.commons.lang.StringUtils;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  /**
38   * Backend based on an unencrypted cookie. Can be used for non sensitive data.
39   * Cookie can be compressed to save bandwidth.
40   * 
41   * <p>
42   * Parameters :
43   * </p>
44   * <ul>
45   * <li>cookiename : name of the cookie</li>
46   * <li>compress : enable cookie compression (gzip) when value is "true"</li>
47   * </ul>
48   * 
49   * @author Nicolas Richeton - Capgemini
50   * 
51   */
52  public class MemcacheBackend extends CookieBackendSupport {
53  	private static final String DESERIALIZE_ERROR = "Cannot deserialize session. A new one will be created"; //$NON-NLS-1$
54  	private static final String ID = "memcache"; //$NON-NLS-1$
55  	private static final String PARAM_SERVER = "server"; //$NON-NLS-1$
56  	private static final char SEPARATOR = ':';
57  	private static final String SESSION_ID = "stateless.memcache.id"; //$NON-NLS-1$
58  	MemcachedClient c = null;
59  	Logger logger = LoggerFactory.getLogger(MemcacheBackend.class);
60  
61  	public MemcacheBackend() {
62  		setCookieName("mid"); //$NON-NLS-1$
63  	}
64  
65  	/**
66  	 * @see com.capgemini.stateless.backend.memcache.ISessionBackend#destroy()
67  	 */
68  	@Override
69  	public void destroy() {
70  		c.shutdown();
71  	}
72  
73  	/**
74  	 * @see com.capgemini.stateless.backend.memcache.ISessionBackend#getId()
75  	 */
76  	@Override
77  	public String getId() {
78  		return ID;
79  	}
80  
81  	/**
82  	 * @see com.capgemini.stateless.backend.memcache.ISessionBackend#init(java.util.Map)
83  	 */
84  	@Override
85  	public void init(Map<String, String> config) throws Exception {
86  		super.init(config);
87  
88  		// Create memcache client.
89  		ArrayList<InetSocketAddress> servers = new ArrayList<InetSocketAddress>();
90  		int i = 1;
91  		String server = null;
92  		String[] memcacheServer = null;
93  		while ((server = config.get(PARAM_SERVER + i)) != null) {
94  			memcacheServer = StringUtils.split(server, SEPARATOR);
95  			servers.add(new InetSocketAddress(memcacheServer[0], Integer
96  					.parseInt(memcacheServer[1])));
97  			i++;
98  			if (logger.isInfoEnabled()) {
99  				logger.info("Adding server: " + memcacheServer[0] + " port: " //$NON-NLS-1$//$NON-NLS-2$
100 						+ memcacheServer[1]);
101 			}
102 		}
103 
104 		// if no configuration, use local server.
105 		if (servers.size() == 0) {
106 			servers.add(new InetSocketAddress("localhost", 11211)); //$NON-NLS-1$
107 		}
108 
109 		// create client
110 		c = new MemcachedClient(servers);
111 	}
112 
113 	/**
114 	 * @see com.capgemini.stateless.backend.memcache.ISessionBackend#restore(javax.servlet.http.HttpServletRequest)
115 	 */
116 	@Override
117 	public ISessionData restore(HttpServletRequest request) {
118 		try {
119 			byte[] data = getCookieData(request, null);
120 
121 			if (data != null) {
122 				String id = new String(data);
123 				request.setAttribute(SESSION_ID, id);
124 
125 				CookieDataSupport s = (CookieDataSupport) c.get(id);
126 				if (s != null && s.isValid()
127 						&& s.getRemoteAddress().equals(getFullRemoteAddr(request))) {
128 					return s;
129 				}
130 			}
131 		} catch (SignatureException e) {
132 			logger.info(DESERIALIZE_ERROR, e);
133 		}
134 		return null;
135 	}
136 
137 	/**
138 	 * @see net.sourceforge.statelessfilter.backend.ISessionBackend#save(net.sourceforge.statelessfilter.backend.ISessionData,
139 	 *      java.util.List, javax.servlet.http.HttpServletRequest,
140 	 *      javax.servlet.http.HttpServletResponse)
141 	 */
142 	@Override
143 	public void save(ISessionData session, List<String> dirtyAttributes,
144 			HttpServletRequest request, HttpServletResponse response)
145 			throws IOException {
146 		try {
147 			if (session != null) {
148 				CookieDataSupport cookieData = new CookieDataSupport(session);
149 				cookieData.setRemoteAddress(getFullRemoteAddr(request));
150 
151 				c.set(session.getId(), 3600, cookieData);
152 
153 				if (request.getAttribute(SESSION_ID) == null) {
154 					setCookieData(request, response, session.getId().getBytes());
155 				}
156 			} else {
157 				setCookieData(request, response, null);
158 			}
159 
160 		} catch (SignatureException e) {
161 			throw new IOException(e);
162 		}
163 	}
164 }