package com.im.imjutil.session;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.im.imjutil.exception.SessionException;
import com.im.imjutil.exception.ValidationException;
import com.im.imjutil.logging.Logger;
import com.im.imjutil.sequence.Sequence;
import com.im.imjutil.sequence.SequenceType;
import com.im.imjutil.util.Runner;
import com.im.imjutil.validation.Convert;
import com.im.imjutil.validation.Validator;
/**
* Classe responsavel por gerenciar sessoes no sistema.
* <br>
* Ele e responsavel por criar e remover sessoes, alem de validar a sessao.
*
* @see {@link Session}, {@link SimpleSession}
* @author Felipe Zappala
*/
public class SessionManager implements Runnable {
/** O tempo padrao de expiracao das sessao em segundos. */
private int time;
/** A thread de teste de sessao expirada */
private Runner runner;
/** O gerador sequencial dos IDs de sessao. */
private Sequence keygen;
/** O tabela de armazenamento das sessoes criadas. */
private Map<String, Session> table;
/** A classe de Implementacao da sessao. */
private Class<? extends Session> implementation;
/**
* Construtor padrao para uma instancia de {@link SessionManager}.
* Este usa classe {@link SimpleSession} de implementacao de {@link Session}.
*/
public SessionManager() {
this(SimpleSession.class);
}
/**
* Construtor padrao para uma instancia de {@link SessionManager}
*
* @param implementation Classe de implementacao de {@link Session}
*/
public SessionManager(Class<? extends Session> implementation) {
if (!Validator.isValid(implementation)) {
throw new ValidationException("Classe de implementacao invalida");
}
this.implementation = implementation;
this.time = (30 * 60); // 30 minutos em segundos
this.table = new HashMap<String, Session>();
this.keygen = Sequence.getInstance(SequenceType.HASH);
this.runner = new Runner(this);
this.runner.setContinuous();
this.runner.setSleep(1000);
}
/**
* Configura o tempo de expiracao padrao para as sessoes criadas.
*
* @return O valor do tempo de expiracao configurado em segundos.
*/
public int getTime() {
return time;
}
/**
* Configura o tempo de expiracao padrao para as sessoes criadas.
* O valor padrao e 30 minutos.
*
* @param time O tempo de expiracao padrao para as sessoes criadas.
*/
public void setTime(int time) {
if (time <= 0) {
throw new ValidationException(Convert.toString(
"Tempo de sessao invalido: ", time));
}
this.time = time;
}
/**
* Configura o tempo de expiracao na sessao passada.
*
* @param time O tempo de expiracao padrao para as sessoes criadas.
* @param session A sessao a ser aplicado o novo tempo.
*/
public void setTime(int time, Session session) {
if (session != null && time > 0) {
session.setExpirationDate(expirationDate(time));
}
}
/**
* Configura o tempo de expiracao na sessao passada.
*
* @param time O tempo de expiracao padrao para as sessoes criadas.
* @param session A sessao a ser aplicado o novo tempo.
*/
public void setTime(int time, String session) {
setTime(time, this.get(session));
}
/**
* Reinicia a sessao passada, reconfigurando com o tempo padrao.
*
* @param session A sessao a ser reiniciada.
*/
public void restart(Session session) {
setTime(this.time, session);
}
/**
* Reinicia a sessao passada, reconfigurando com o tempo padrao.
*
* @param session A sessao a ser reiniciada.
*/
public void restart(String session) {
setTime(this.time, this.get(session));
}
/**
* Cria uma nova sessao com o tempo de expiracao passado.
*
* @param time O tempo de expiracao.
* @return A sessao criada.
* @throws SessionException Caso ocorra algum erro ao criar a sessao.
*/
public Session create(int time) throws SessionException {
try {
if (time <= 0) {
throw new ValidationException(Convert.toString(
"Tempo de sessao invalido: ", time));
}
Session session = implementation.newInstance();
session.setExpirationDate(expirationDate(time));
session.setId(createId());
add(session);
return session;
} catch (Exception e) {
throw new SessionException("Erro ao criar sessao", e);
}
}
/**
* Cria uma nova sessao com o tempo de expiracao padrao de 30 minutos.
*
* @return A sessao criada.
* @throws SessionException Caso ocorra algum erro ao criar a sessao.
*/
public Session create() throws SessionException {
return create(time);
}
/**
* Cria uma nova sessao com o tempo de expiracao padrao de 30 minutos.
* <p>
* ATENCAO: Este metodo aplica o cast automaticamente para o objeto de
* retorno, contudo se o objeto criado nao for do mesmo tipo do esperado
* no retorno, sera gerada uma excessao do tipo {@link ClassCastException}.
* </p>
* @return A sessao criada.
* @throws SessionException Caso ocorra algum erro ao criar a sessao.
*/
@SuppressWarnings("unchecked")
public <T extends Session> T createSession() throws SessionException {
return (T) create(time);
}
/**
* Cria uma nova sessao com o tempo de expiracao passado.
* <p>
* ATENCAO: Este metodo aplica o cast automaticamente para o objeto de
* retorno, contudo se o objeto criado nao for do mesmo tipo do esperado
* no retorno, sera gerada uma excessao do tipo {@link ClassCastException}.
* </p>
* @param time O tempo de expiracao.
* @return A sessao criada.
* @throws SessionException Caso ocorra algum erro ao criar a sessao.
*/
@SuppressWarnings("unchecked")
public <T extends Session> T createSession(int time)
throws SessionException {
return (T) create(time);
}
/**
* Obtem uma sessao do gerenciador.
*
* @param session A sessao a ser obtida.
* @return A sessao caso encontrado, senao, {@code null}.
*/
public Session get(String session) {
validateParam(session);
Session ss = this.table.get(session);
if (ss == null) {
return new InvalidSession(session);
}
return ss;
}
/**
* Obtem uma sessao do gerenciador.
*
* @param session A sessao a ser obtida.
* @return A sessao caso encontrado, senao, {@code null}.
*/
@SuppressWarnings("unchecked")
public <T extends Session> T getSession(String session) {
return (T) get(session);
}
/**
* Retorna o conjunto das sessoes ativas no gerenciador.
*
* @return Um conjunto das sessoes ativas.
*/
public Collection<Session> getSessions() {
return Collections.unmodifiableCollection(this.table.values());
}
/**
* Adiciona uma sessao do gerenciador.
*
* @param session A sessao a ser adicionada.
*/
protected void add(Session session) {
validateParam(session);
this.table.put(session.getId(), session);
}
/**
* Remove uma sessao do gerenciador.
*
* @param session A sessao a ser removida.
*/
protected void remove(Session session) {
validateParam(session);
this.table.remove(session);
}
/**
* Verifica se a sessao e valida no gerenciador atual.
*
* @param session A sessao a ser verificada.
* @return Verdadeiro caso a sessao for valida e estiver ativa.
*/
public boolean isValid(Session session) {
validateParam(session);
return this.table.containsKey(session.getId()) && session.isValid();
}
/**
* Verifica se a sessao e valida no gerenciador atual.
*
* @param session A sessao a ser verificada.
* @return Verdadeiro caso a sessao for valida e estiver ativa.
*/
public boolean isValid(String session) {
validateParam(session);
Session s = this.table.get(session);
if (!Validator.isValid(s)) {
return false;
}
return isValid(s);
}
/**
* Invalida uma sessao no gerenciador.
*
* @param session A sessao a ser invalidada.
*/
public void invalidate(Session session) {
this.remove(session);
session.invalidate();
}
/**
* Invalida uma sessao no gerenciador.
*
* @param session A sessao a ser invalidada.
*/
public void invalidate(String session) {
this.invalidate(this.get(session));
}
/**
* Verifica se os parametros passados sao validos, ou seja,
* nao nulos ou vazios. Caso forem invalidos, sera lancado uma excecao
* nao verificado de validacao.
*
* @param obj Os parametros a serem validados.
* @throws ValidationException A excessao lancada.
*/
protected static void validateParam(Object... obj)
throws ValidationException {
if (!Validator.isValid(obj)) {
throw new ValidationException("Parametro passado e nulo ou vazio");
}
}
/**
* Cria a data de expiracao a partir do tempo em segundo passado,
* baseato do tempo corrente da maquina.
* <br>
* {@code tempo_atual + tempo_passado = tempo_expiracao}
*
* @param seconds
* @return
*/
protected Date expirationDate(int seconds) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, seconds);
return calendar.getTime();
}
/**
* Cria um novo ID unico de sessao.
*
* @return O ID de sessao criado.
*/
protected String createId() {
return keygen.next();
}
/**
* Executa a validacao das sessoes no gerenciador.
* <br>
* Roda em ciclo infinito, testando a cada segundo as sessoes
* gerenciadas e invalida aquelas que estourarem o seu tempo.
*/
@Override
public void run() {
Date currentDate = new Date();
Set<String> keys = table.keySet();
Logger.debug("[SessionManager] Tamanho sessao:", keys.size());
Session currentSession;
for (String sessionId : keys) {
currentSession = table.get(sessionId);
if (currentSession.getExpirationDate().before(currentDate)) {
Logger.debug("[SessionManager] Removendo sessao: ", sessionId);
table.remove(sessionId);
}
}
}
public void start() {
this.runner.start();
}
public void stop() {
this.runner.stop();
}
}