PageRenderTime 39ms CodeModel.GetById 2ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 1ms

/hazelcast-wm/src/main/java/com/hazelcast/web/WebFilter.java

https://bitbucket.org/gabral6_gmailcom/hazelcast
Java | 651 lines | 529 code | 96 blank | 26 comment | 133 complexity | f27774a12fe115a5600a044eb19c8291 MD5 | raw file
  1/*
  2 * Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
  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
 17package com.hazelcast.web;
 18
 19import com.hazelcast.config.Config;
 20import com.hazelcast.config.MapConfig;
 21import com.hazelcast.core.*;
 22import com.hazelcast.impl.ThreadContext;
 23import com.hazelcast.logging.ILogger;
 24import com.hazelcast.logging.Logger;
 25import com.hazelcast.nio.Data;
 26import com.hazelcast.util.Clock;
 27
 28import javax.servlet.*;
 29import javax.servlet.http.*;
 30import java.io.IOException;
 31import java.io.NotSerializableException;
 32import java.io.Serializable;
 33import java.util.*;
 34import java.util.concurrent.ConcurrentHashMap;
 35import java.util.concurrent.ConcurrentMap;
 36import java.util.logging.Level;
 37
 38import static com.hazelcast.web.HazelcastInstanceLoader.*;
 39
 40public class WebFilter implements Filter {
 41
 42    private static final ILogger logger = Logger.getLogger(WebFilter.class.getName());
 43
 44    private static final String HAZELCAST_REQUEST = "*hazelcast-request";
 45
 46    private static final String HAZELCAST_SESSION_COOKIE_NAME = "hazelcast.sessionId";
 47
 48    private static final ConcurrentMap<String, String> mapOriginalSessions = new ConcurrentHashMap<String, String>(1000);
 49
 50    private static final ConcurrentMap<String, HazelcastHttpSession> mapSessions = new ConcurrentHashMap<String, HazelcastHttpSession>(1000);
 51
 52    private HazelcastInstance hazelcastInstance;
 53
 54    private String clusterMapName = "none";
 55
 56    private String sessionCookieName = HAZELCAST_SESSION_COOKIE_NAME;
 57
 58    private String sessionCookieDomain = null;
 59
 60    private boolean sessionCookieSecure = false;
 61
 62    private boolean sessionCookieHttpOnly = false;
 63
 64    private boolean stickySession = true;
 65
 66    private boolean debug = false;
 67
 68    private boolean shutdownOnDestroy = true;
 69
 70    private Properties properties;
 71
 72    protected ServletContext servletContext;
 73
 74    protected FilterConfig filterConfig;
 75
 76    public WebFilter() {
 77    }
 78
 79    public WebFilter(Properties properties) {
 80        this();
 81        this.properties = properties;
 82    }
 83
 84    public final void init(final FilterConfig config) throws ServletException {
 85        filterConfig = config;
 86        servletContext = config.getServletContext();
 87        initInstance();
 88        String debugParam = getParam("debug");
 89        if (debugParam != null) {
 90            debug = Boolean.valueOf(debugParam);
 91        }
 92        String mapName = getParam("map-name");
 93        if (mapName != null) {
 94            clusterMapName = mapName;
 95        } else {
 96            clusterMapName = "_web_" + servletContext.getServletContextName();
 97        }
 98        Config hzConfig = hazelcastInstance.getConfig();
 99        String sessionTTL = getParam("session-ttl-seconds");
100        if (sessionTTL != null) {
101            MapConfig mapConfig = new MapConfig(clusterMapName);
102            mapConfig.setTimeToLiveSeconds(Integer.valueOf(sessionTTL));
103            hzConfig.addMapConfig(mapConfig);
104        }
105        String cookieName = getParam("cookie-name");
106        if (cookieName != null) {
107            sessionCookieName = cookieName;
108        }
109        String cookieDomain = getParam("cookie-domain");
110        if (cookieDomain != null) {
111            sessionCookieDomain = cookieDomain;
112        }
113        String cookieSecure = getParam("cookie-secure");
114        if (cookieSecure != null) {
115            sessionCookieSecure = Boolean.valueOf(cookieSecure);
116        }
117        String cookieHttpOnly = getParam("cookie-http-only");
118        if (cookieHttpOnly != null) {
119            sessionCookieHttpOnly = Boolean.valueOf(cookieHttpOnly);
120        }
121        String stickySessionParam = getParam("sticky-session");
122        if (stickySessionParam != null) {
123            stickySession = Boolean.valueOf(stickySessionParam);
124        }
125        String shutdownOnDestroyParam = getParam("shutdown-on-destroy");
126        if (shutdownOnDestroyParam != null) {
127            shutdownOnDestroy = Boolean.valueOf(shutdownOnDestroyParam);
128        }
129        if (!stickySession) {
130            getClusterMap().addEntryListener(new EntryListener() {
131                public void entryAdded(EntryEvent entryEvent) {
132                }
133
134                public void entryRemoved(EntryEvent entryEvent) {
135                    if (entryEvent.getMember() == null || // client events has no owner member
136                            !entryEvent.getMember().localMember()) {
137                        removeSessionLocally((String) entryEvent.getKey());
138                    }
139                }
140
141                public void entryUpdated(EntryEvent entryEvent) {
142                    if (entryEvent.getMember() == null || // client events has no owner member
143                            !entryEvent.getMember().localMember()) {
144                        markSessionDirty((String) entryEvent.getKey());
145                    }
146                }
147
148                public void entryEvicted(EntryEvent entryEvent) {
149                    entryRemoved(entryEvent);
150                }
151            }, false);
152        }
153        log("sticky:" + stickySession + ", debug: " + debug + ", shutdown-on-destroy: " + shutdownOnDestroy
154                + ", map-name: " + clusterMapName);
155    }
156
157    private void initInstance() throws ServletException {
158        if (properties == null) {
159            properties = new Properties();
160        }
161        setProperty(CONFIG_LOCATION);
162        setProperty(INSTANCE_NAME);
163        setProperty(USE_CLIENT);
164        setProperty(CLIENT_CONFIG_LOCATION);
165        hazelcastInstance = (HazelcastInstance) getInstance(properties);
166    }
167
168    private void setProperty(String propertyName) {
169        String value = getParam(propertyName);
170        if (value != null) {
171            properties.setProperty(propertyName, value);
172        }
173    }
174
175    private void removeSessionLocally(String sessionId) {
176        HazelcastHttpSession hazelSession = mapSessions.remove(sessionId);
177        if (hazelSession != null) {
178            mapOriginalSessions.remove(hazelSession.originalSession.getId());
179            log("Destroying session locally " + hazelSession);
180            hazelSession.destroy();
181        }
182    }
183
184    private void markSessionDirty(String sessionId) {
185        HazelcastHttpSession hazelSession = mapSessions.get(sessionId);
186        if (hazelSession != null) {
187            hazelSession.setDirty(true);
188        }
189    }
190
191    static void destroyOriginalSession(HttpSession originalSession) {
192        String hazelcastSessionId = mapOriginalSessions.remove(originalSession.getId());
193        if (hazelcastSessionId != null) {
194            HazelcastHttpSession hazelSession = mapSessions.remove(hazelcastSessionId);
195            if (hazelSession != null) {
196                hazelSession.webFilter.destroySession(hazelSession, false);
197            }
198        }
199    }
200
201    protected void log(final Object obj) {
202        Level level = Level.FINEST;
203        if (debug) {
204            level = Level.INFO;
205        }
206        logger.log(level, obj.toString());
207    }
208
209    private HazelcastHttpSession createNewSession(RequestWrapper requestWrapper, String existingSessionId) {
210        String id = existingSessionId != null ? existingSessionId : generateSessionId();
211        if (requestWrapper.getOriginalSession(false) != null) {
212            log("Original session exists!!!");
213        }
214        HttpSession originalSession = requestWrapper.getOriginalSession(true);
215        HazelcastHttpSession hazelcastSession = new HazelcastHttpSession(WebFilter.this, id, originalSession);
216        mapSessions.put(hazelcastSession.getId(), hazelcastSession);
217        String oldHazelcastSessionId = mapOriginalSessions.put(originalSession.getId(), hazelcastSession.getId());
218        if (oldHazelcastSessionId != null) {
219            log("!!! Overriding an existing hazelcastSessionId " + oldHazelcastSessionId);
220        }
221        log("Created new session with id: " + id);
222        log(mapSessions.size() + " is sessions.size and originalSessions.size: " + mapOriginalSessions.size());
223        addSessionCookie(requestWrapper, id);
224        return hazelcastSession;
225    }
226
227    /**
228     * Destroys a session, determining if it should be destroyed clusterwide automatically or via expiry.
229     *
230     * @param session             The session to be destroyed
231     * @param removeGlobalSession boolean value - true if the session should be destroyed irrespective of active time
232     */
233    private void destroySession(HazelcastHttpSession session, boolean removeGlobalSession) {
234        log("Destroying local session: " + session.getId());
235        mapSessions.remove(session.getId());
236        mapOriginalSessions.remove(session.originalSession.getId());
237        session.destroy();
238        if (removeGlobalSession) {
239            log("Destroying cluster session: " + session.getId() + " => Ignore-timeout: true");
240            getClusterMap().remove(session.getId());
241        }
242    }
243
244    private IMap getClusterMap() {
245        return hazelcastInstance.getMap(clusterMapName);
246    }
247
248    private HazelcastHttpSession getSessionWithId(final String sessionId) {
249        HazelcastHttpSession session = mapSessions.get(sessionId);
250        if (session != null && !session.isValid()) {
251            session = null;
252            destroySession(session, true);
253        }
254        return session;
255    }
256
257    private class RequestWrapper extends HttpServletRequestWrapper {
258        HazelcastHttpSession hazelcastSession = null;
259
260        final ResponseWrapper res;
261
262        String requestedSessionId;
263
264        public RequestWrapper(final HttpServletRequest req,
265                              final ResponseWrapper res) {
266            super(req);
267            this.res = res;
268            req.setAttribute(HAZELCAST_REQUEST, this);
269        }
270
271        public void setHazelcastSession(HazelcastHttpSession hazelcastSession, String requestedSessionId) {
272            this.hazelcastSession = hazelcastSession;
273            this.requestedSessionId = requestedSessionId;
274        }
275
276        HttpSession getOriginalSession(boolean create) {
277            return super.getSession(create);
278        }
279
280        @Override
281        public RequestDispatcher getRequestDispatcher(final String path) {
282            final ServletRequest original = getRequest();
283            return new RequestDispatcher() {
284                public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
285//                    original.getRequestDispatcher(path).forward(original, servletResponse);
286                    original.getRequestDispatcher(path).forward(RequestWrapper.this, servletResponse);
287                }
288
289                public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
290//                    original.getRequestDispatcher(path).include(original, servletResponse);
291                    original.getRequestDispatcher(path).include(RequestWrapper.this, servletResponse);
292                }
293            };
294        }
295
296        public String fetchHazelcastSessionId() {
297            if (requestedSessionId != null) {
298                return requestedSessionId;
299            }
300            requestedSessionId = getSessionCookie(this);
301            return requestedSessionId;
302        }
303
304        @Override
305        public HttpSession getSession() {
306            return getSession(true);
307        }
308
309        @Override
310        public HazelcastHttpSession getSession(final boolean create) {
311            if (hazelcastSession != null && !hazelcastSession.isValid()) {
312                log("Session is invalid!");
313                destroySession(hazelcastSession, true);
314                hazelcastSession = null;
315            }
316            if (hazelcastSession == null) {
317                HttpSession originalSession = getOriginalSession(false);
318                if (originalSession != null) {
319                    String hazelcastSessionId = mapOriginalSessions.get(originalSession.getId());
320                    if (hazelcastSessionId != null) {
321                        hazelcastSession = mapSessions.get(hazelcastSessionId);
322                    }
323                    if (hazelcastSession == null) {
324                        mapOriginalSessions.remove(originalSession.getId());
325                        originalSession.invalidate();
326                    } else if (hazelcastSession.isDirty()) {
327                        hazelcastSession = null;
328                    }
329                }
330            }
331            if (hazelcastSession != null)
332                return hazelcastSession;
333            final String requestedSessionId = fetchHazelcastSessionId();
334            if (requestedSessionId != null) {
335                hazelcastSession = getSessionWithId(requestedSessionId);
336                if (hazelcastSession == null) {
337                    final Map mapSession = (Map) getClusterMap().get(requestedSessionId);
338                    if (mapSession != null) {
339                        // we already have the session in the cluster
340                        // loading it...
341                        hazelcastSession = createNewSession(RequestWrapper.this, requestedSessionId);
342                        overrideSession(hazelcastSession, mapSession);
343                    }
344                }
345            }
346            if (hazelcastSession == null && create) {
347                hazelcastSession = createNewSession(RequestWrapper.this, null);
348            } else if (hazelcastSession != null && !stickySession && requestedSessionId != null && hazelcastSession.isDirty()) {
349                log(requestedSessionId + " is dirty reloading.");
350                final Map mapSession = (Map) getClusterMap().get(requestedSessionId);
351                overrideSession(hazelcastSession, mapSession);
352            }
353            return hazelcastSession;
354        }
355
356        private void overrideSession(HazelcastHttpSession session, Map mapSession) {
357            if (session == null || mapSession == null) return;
358            final Enumeration<String> atts = session.getAttributeNames();
359            while (atts.hasMoreElements()) {
360                session.removeAttribute(atts.nextElement());
361            }
362            Map mapData = null;
363            final Set<Map.Entry> entries = mapSession.entrySet();
364            for (final Map.Entry entry : entries) {
365                session.setAttribute((String) entry.getKey(), entry.getValue());
366                if (mapData == null) {
367                    mapData = new HashMap<String, Object>();
368                }
369                mapData.put(entry.getKey(), entry.getValue());
370            }
371            session.sessionChanged(session.writeObject(mapData));
372            session.setDirty(false);
373        }
374    } // END of RequestWrapper
375
376    private class ResponseWrapper extends HttpServletResponseWrapper {
377
378        RequestWrapper req = null;
379
380        public ResponseWrapper(final HttpServletResponse original) {
381            super(original);
382        }
383
384        public void setRequest(final RequestWrapper req) {
385            this.req = req;
386        }
387    }
388
389    private class HazelcastHttpSession implements HttpSession {
390        private Data currentSessionData = null;
391
392        volatile boolean valid = true;
393
394        volatile boolean dirty = false;
395
396        final String id;
397
398        final HttpSession originalSession;
399
400        final WebFilter webFilter;
401
402        private final AtomicNumber timestamp;
403
404        public HazelcastHttpSession(WebFilter webFilter, final String sessionId, HttpSession originalSession) {
405            this.webFilter = webFilter;
406            this.id = sessionId;
407            this.originalSession = originalSession;
408            timestamp = hazelcastInstance.getAtomicNumber(clusterMapName + "_" + id);
409        }
410
411        public Object getAttribute(final String name) {
412            return originalSession.getAttribute(name);
413        }
414
415        public Enumeration getAttributeNames() {
416            return originalSession.getAttributeNames();
417        }
418
419        public String getId() {
420            return id;
421        }
422
423        public ServletContext getServletContext() {
424            return servletContext;
425        }
426
427        public HttpSessionContext getSessionContext() {
428            return originalSession.getSessionContext();
429        }
430
431        public Object getValue(final String name) {
432            return getAttribute(name);
433        }
434
435        public String[] getValueNames() {
436            return originalSession.getValueNames();
437        }
438
439        public boolean isDirty() {
440            return dirty;
441        }
442
443        public void setDirty(boolean dirty) {
444            this.dirty = dirty;
445        }
446
447        public void invalidate() {
448            originalSession.invalidate();
449            destroySession(this, true);
450        }
451
452        public boolean isNew() {
453            return originalSession.isNew();
454        }
455
456        public void putValue(final String name, final Object value) {
457            setAttribute(name, value);
458        }
459
460        public void removeAttribute(final String name) {
461            originalSession.removeAttribute(name);
462        }
463
464        public void setAttribute(final String name, final Object value) {
465            if (value != null && !(value instanceof Serializable)) {
466                throw new IllegalArgumentException(new NotSerializableException(value.getClass().getName()));
467            }
468            originalSession.setAttribute(name, value);
469        }
470
471        public void removeValue(final String name) {
472            removeAttribute(name);
473        }
474
475        public boolean sessionChanged(final Data data) {
476            try {
477                if (data == null) {
478                    return currentSessionData != null;
479                }
480                return currentSessionData == null || !data.equals(currentSessionData);
481            } finally {
482                currentSessionData = data;
483            }
484        }
485
486        public long getCreationTime() {
487            return originalSession.getCreationTime();
488        }
489
490        public long getLastAccessedTime() {
491            return originalSession.getLastAccessedTime();
492        }
493
494        public int getMaxInactiveInterval() {
495            return originalSession.getMaxInactiveInterval();
496        }
497
498        public void setMaxInactiveInterval(int maxInactiveSeconds) {
499            originalSession.setMaxInactiveInterval(maxInactiveSeconds);
500        }
501
502        public synchronized Data writeObject(final Object obj) {
503            if (obj == null)
504                return null;
505            return ThreadContext.get().toData(obj);
506        }
507
508        void destroy() {
509            valid = false;
510            timestamp.destroy();
511        }
512
513        public boolean isValid() {
514            return valid;
515        }
516
517        void setAccessed() {
518            timestamp.set(Clock.currentTimeMillis());
519        }
520
521        long getLastAccessed() {
522            return hazelcastInstance.getLifecycleService().isRunning()
523                    ? timestamp.get() : 0L;
524        }
525    }// END of HazelSession
526
527    private static synchronized String generateSessionId() {
528        final String id = UUID.randomUUID().toString();
529        final StringBuilder sb = new StringBuilder("HZ");
530        final char[] chars = id.toCharArray();
531        for (final char c : chars) {
532            if (c != '-') {
533                if (Character.isLetter(c)) {
534                    sb.append(Character.toUpperCase(c));
535                } else
536                    sb.append(c);
537            }
538        }
539        return sb.toString();
540    }
541
542    private void addSessionCookie(final RequestWrapper req, final String sessionId) {
543        final Cookie sessionCookie = new Cookie(sessionCookieName, sessionId);
544        String path = req.getContextPath();
545        if ("".equals(path)) {
546            path = "/";
547        }
548        sessionCookie.setPath(path);
549        sessionCookie.setMaxAge(-1);
550        if (sessionCookieDomain != null) {
551            sessionCookie.setDomain(sessionCookieDomain);
552        }
553		try {
554			sessionCookie.setHttpOnly(sessionCookieHttpOnly);
555		} catch (NoSuchMethodError e) {
556			// must be servlet spec before 3.0, don't worry about it!
557		}
558        sessionCookie.setSecure(sessionCookieSecure);
559        req.res.addCookie(sessionCookie);
560    }
561
562    private String getSessionCookie(final RequestWrapper req) {
563        final Cookie[] cookies = req.getCookies();
564        if (cookies != null) {
565            for (final Cookie cookie : cookies) {
566                final String name = cookie.getName();
567                final String value = cookie.getValue();
568                if (name.equalsIgnoreCase(sessionCookieName)) {
569                    return value;
570                }
571            }
572        }
573        return null;
574    }
575
576    public final void doFilter(ServletRequest req, ServletResponse res, final FilterChain chain)
577            throws IOException, ServletException {
578        if (!(req instanceof HttpServletRequest)) {
579            chain.doFilter(req, res);
580        } else {
581            if (req instanceof RequestWrapper) {
582                log("Request is instance of RequestWrapper! Continue...");
583                chain.doFilter(req, res);
584                return;
585            }
586            HttpServletRequest httpReq = (HttpServletRequest) req;
587            RequestWrapper existingReq = (RequestWrapper) req.getAttribute(HAZELCAST_REQUEST);
588            final ResponseWrapper resWrapper = new ResponseWrapper((HttpServletResponse) res);
589            final RequestWrapper reqWrapper = new RequestWrapper(httpReq, resWrapper);
590            resWrapper.setRequest(reqWrapper);
591            if (existingReq != null) {
592                reqWrapper.setHazelcastSession(existingReq.hazelcastSession, existingReq.requestedSessionId);
593            }
594            req = null;
595            res = null;
596            httpReq = null;
597            chain.doFilter(reqWrapper, resWrapper);
598            if (existingReq != null) return;
599            req = null; // for easy debugging. reqWrapper should be used
600            HazelcastHttpSession session = reqWrapper.getSession(false);
601            if (session != null && session.isValid()) {
602                session.setAccessed();
603                final Enumeration<String> attNames = session.getAttributeNames();
604                Map mapData = null;
605                while (attNames.hasMoreElements()) {
606                    final String attName = attNames.nextElement();
607                    final Object value = session.getAttribute(attName);
608                    if (mapData == null) {
609                        mapData = new HashMap<String, Object>();
610                    }
611                    mapData.put(attName, value);
612                }
613                Data data = session.writeObject(mapData);
614                boolean sessionChanged = session.sessionChanged(data);
615                if (sessionChanged) {
616                    if (data == null) {
617                        mapData = new HashMap<String, Object>();
618                        data = session.writeObject(mapData);
619                    }
620                    log("PUTTING SESSION " + session.getId());
621                    getClusterMap().put(session.getId(), data);
622                }
623            }
624        }
625    }
626
627    public final void destroy() {
628        mapSessions.clear();
629        mapOriginalSessions.clear();
630        shutdownInstance();
631    }
632
633    protected HazelcastInstance getInstance(Properties properties) throws ServletException {
634        return HazelcastInstanceLoader.createInstance(filterConfig, properties);
635    }
636
637    protected void shutdownInstance() {
638        if (shutdownOnDestroy && hazelcastInstance != null) {
639            hazelcastInstance.getLifecycleService().shutdown();
640        }
641    }
642
643    private String getParam(String name) {
644        if (properties != null && properties.containsKey(name)) {
645            return properties.getProperty(name);
646        } else {
647            return filterConfig.getInitParameter(name);
648        }
649    }
650}// END of WebFilter
651