com.sector91.wit.http.Session.java Source code

Java tutorial

Introduction

Here is the source code for com.sector91.wit.http.Session.java

Source

// ~~~~~~~~~~~~~~~~~~~~~~~~~~ //

////   ///   /// ///       
//////  ////   // ////  //// 
/// ////  ////  //  ////  //// 
///  //// /////  //        ///  
///  //// ///// //  //// ////// 
//   /// /////  //  ////  ////  
// //// ///// //  ////  ////   
/////////////  ////  ////   
////////////   ///   ///    
///// /////   ////  ////    
///// /////   //// ///// // 
////  ////    /// ///// //  
///// /////   ////////////   
////  ////     ////  ////    

// The Web framework with class.

// ~~~~~~~~~~~~~~~~~~~~~~~~~~ //

// Copyright (c) 2013 Adam R. Nelson
//
// 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 com.sector91.wit.http;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/*>>> import checkers.nullness.quals.Nullable;*/

import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.simpleframework.http.Cookie;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;

import com.google.common.base.Optional;
import com.sector91.wit.WebApp;
import com.sector91.wit.data.DataException;
import com.sector91.wit.data.NotFoundException;
import com.sector91.wit.data.Store;

public class Session implements Serializable {

    private static final long serialVersionUID = 1L;

    public static final String COOKIE_NAME = "WITSESSIONID";
    public static final Duration DEFAULT_LIFETIME = new Duration(15 * 60 * 1000); // 15 minutes

    private static final Map<Class<? extends WebApp>, Store<Long, Session>> sessionStores = new HashMap<>();
    private static final AtomicLong nextId = new AtomicLong(0);

    private static Store<Long, Session> getStore(Request request) {
        @SuppressWarnings("unchecked")
        final Class<? extends WebApp> cls = (Class<? extends WebApp>) request.getAttribute(WebApp.class);
        if (sessionStores.containsKey(cls)) {
            return sessionStores.get(cls);
        } else {
            final Store<Long, Session> store = new MemorySessionStore();
            sessionStores.put(cls, store);
            return store;
        }
    }

    public static void useSessionStore(WebApp app, Store<Long, Session> store) {
        sessionStores.put(app.getClass(), store);
    }

    public static Session get(Request request) throws NotFoundException {
        final Cookie sessionCookie = request.getCookie(COOKIE_NAME);
        if (sessionCookie == null)
            throw new NotFoundException("Request does not contain a session ID.");
        final long id;
        try {
            id = Long.parseLong(sessionCookie.getValue(), 16);
        } catch (NumberFormatException ex) {
            throw new NotFoundException("Request session ID is improperly formatted.");
        }
        final Store<Long, Session> store = getStore(request);
        final Session session;
        try {
            session = store.read(id);
        } catch (NotFoundException ex) {
            throw ex;
        } catch (DataException ex) {
            throw new RuntimeException(ex); // TODO: Handle DataExceptions more intelligently.
        }
        session.touch();
        if (session.isExpired()) {
            try {
                store.delete(id);
            } catch (NotFoundException ex) {
                // Do nothing.
            } catch (DataException ex) {
                throw new RuntimeException(ex);
            }
            throw new NotFoundException("Session has expired.");
        }
        return session;
    }

    public static Session create(Request request, Response response) {
        final Store<Long, Session> store = getStore(request);
        final Session newSession = new Session(nextId.getAndIncrement(), store);
        try {
            store.create(newSession);
        } catch (DataException ex) {
            throw new RuntimeException(ex);
        }
        response.setCookie(COOKIE_NAME, newSession.idString());
        return newSession;
    }

    public static Session getOrCreate(Request request, Response response) {
        try {
            return get(request);
        } catch (NotFoundException ex) {
            return create(request, response);
        }
    }

    private final long id;
    private Duration lifetime;
    private DateTime expiresAt;
    private final Map<Class<? extends SessionData<?>>, SessionData<?>> data;
    private final Store<Long, Session> store;

    protected Session(long id, Store<Long, Session> store) {
        this.id = id;
        this.data = new HashMap<>();
        this.lifetime = DEFAULT_LIFETIME;
        this.expiresAt = DateTime.now().plus(lifetime);
        this.store = store;
    }

    public long id() {
        return id;
    }

    public String idString() {
        return Long.toHexString(id);
    }

    @SuppressWarnings("unchecked")
    public <D extends SessionData<D>> Optional<D> get(Class<D> dataClass) {
        return Optional.fromNullable((D) data.get(dataClass).clone());
    }

    public boolean has(Class<? extends SessionData<?>> dataClass) {
        return data.containsKey(dataClass);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void put(SessionData<?> data) {
        put((Class) data.getClass(), (SessionData) data);
    }

    public <D extends SessionData<D>> void put(Class<D> dataClass, D data) {
        this.data.put(dataClass, data.clone());
        try {
            store.update(id, this);
        } catch (DataException ex) {
            throw new RuntimeException(ex);
        }
    }

    public boolean remove(Class<? extends SessionData<?>> dataClass) {
        boolean result = this.data.remove(dataClass) != null;
        try {
            store.update(id, this);
        } catch (DataException ex) {
            throw new RuntimeException(ex);
        }
        return result;
    }

    public void touch() {
        this.expiresAt = DateTime.now().plus(lifetime);
    }

    public final void expireAfter(long quantity, TimeUnit unit) {
        expireAfter(new Duration(unit.toMillis(quantity)));
        try {
            store.update(id, this);
        } catch (DataException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void expireAfter(Duration duration) {
        lifetime = duration;
        touch();
    }

    public boolean isExpired() {
        return DateTime.now().isAfter(expiresAt);
    }
}