com.alfaariss.oa.engine.session.jdbc.JDBCSessionFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.alfaariss.oa.engine.session.jdbc.JDBCSessionFactory.java

Source

/*
 * * Asimba - Serious Open Source SSO
 * 
 * Copyright (C) 2012 Asimba
 * Copyright (C) 2007-2009 Alfa & Ariss B.V.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see www.gnu.org/licenses
 * 
 * * Asimba - Serious Open Source SSO - More information on www.asimba.org
 * 
 */
package com.alfaariss.oa.engine.session.jdbc;

import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;
import java.util.Locale;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;

import com.alfaariss.oa.OAException;
import com.alfaariss.oa.SystemErrors;
import com.alfaariss.oa.UserEvent;
import com.alfaariss.oa.api.configuration.ConfigurationException;
import com.alfaariss.oa.api.datastorage.IDataStorageFactory;
import com.alfaariss.oa.api.persistence.IEntity;
import com.alfaariss.oa.api.persistence.IEntityManager;
import com.alfaariss.oa.api.persistence.PersistenceException;
import com.alfaariss.oa.api.session.ISession;
import com.alfaariss.oa.api.session.SessionState;
import com.alfaariss.oa.api.storage.IStorageFactory;
import com.alfaariss.oa.api.storage.clean.ICleanable;
import com.alfaariss.oa.api.user.IUser;
import com.alfaariss.oa.engine.core.Engine;
import com.alfaariss.oa.engine.core.attribute.SessionAttributes;
import com.alfaariss.oa.engine.core.authentication.AuthenticationProfile;
import com.alfaariss.oa.engine.core.session.SessionException;
import com.alfaariss.oa.engine.core.session.factory.ISessionFactory;
import com.alfaariss.oa.util.ModifiedBase64;
import com.alfaariss.oa.util.Serialize;
import com.alfaariss.oa.util.database.DatabaseException;
import com.alfaariss.oa.util.database.jdbc.DataSourceFactory;
import com.alfaariss.oa.util.logging.UserEventLogItem;
import com.alfaariss.oa.util.storage.factory.AbstractStorageFactory;

/**
 * <code>ISessionFactory</code> which uses a JDBC source for storage.
 * 
 * @author MHO
 * @author EVB
 * @author Alfa & Ariss
 *
 */
public class JDBCSessionFactory extends AbstractStorageFactory implements ISessionFactory<JDBCSession> {
    private final static String TABLE_NAME = "session";
    private final static String COLUMN_ID = "id";
    private final static String COLUMN_TGT_ID = "tgt_id";
    private final static String COLUMN_STATE = "state";
    private final static String COLUMN_REQUESTOR = "requestor_id";
    private final static String COLUMN_URL = "url";
    private final static String COLUMN_EXPIRATION = "expiration";
    private final static String COLUMN_FORCED_AUTHENTICATE = "forced_authenticate";
    private final static String COLUMN_OWNER = "sessionuser";
    private final static String COLUMN_ATTRIBUTES = "attributes";
    private final static String COLUMN_FORCED_USERID = "forced_userid";
    private final static String COLUMN_LOCALE = "locale";
    private final static String COLUMN_SELECTED_AUTHN_PROFILE = "selected_authn_profile";
    private final static String COLUMN_AUTHN_PROFILES = "authn_profiles";
    private final static String COLUMN_PASSIVE = "passive";

    private String _sTableName;
    private String _sColumnID;
    private String _sColumnTGT_ID;
    private String _sColumnSTATE;
    private String _sColumnREQUESTOR;
    private String _sColumnURL;
    private String _sColumnEXPIRATION;
    private String _sColumnFORCED_AUTHENTICATE;
    private String _sColumnOWNER;
    private String _sColumnATTRIBUTES;
    private String _sColumnFORCED_USERID;
    private String _sColumnLOCALE;
    private String _sColumnSELECTED_AUTHN_PROFILE;
    private String _sColumnAUTHN_PROFILES;
    private String _sColumnPASSIVE;

    //Queries
    private String _sSearchQuery = null;
    private String _sCountQuery = null;
    private String _sInsertQuery = null;
    private String _sUpdateQuery = null;
    private String _sRemoveQuery = null;
    private String _sRemoveExpiredQuery = null;

    //The JDBC manager 
    private DataSource _oDataSource;
    //The system logger
    private Log _logger;
    private Log _eventLogger;

    /**
      * Create a new <code>JDBCFactory</code>.
      */
    public JDBCSessionFactory() {
        super();
        _logger = LogFactory.getLog(JDBCSessionFactory.class);
        _eventLogger = LogFactory.getLog(Engine.EVENT_LOGGER);
    }

    /**
     * Call super class and start cleaner.
     * @see IStorageFactory#start()
     */
    public void start() throws OAException {

        Element eResource = _configurationManager.getSection(_eConfig, "resource");
        if (eResource != null) {
            _oDataSource = DataSourceFactory.createDataSource(_configurationManager, eResource);
            _logger.info("Using datasource specified in 'resource' section in configuration");
        } else {
            IDataStorageFactory databaseFactory = Engine.getInstance().getStorageFactory();
            if (databaseFactory != null && databaseFactory.isEnabled()) {
                _oDataSource = databaseFactory.createSystemDatasource();
                if (_oDataSource == null) {
                    _logger.error("Could not create a valid datasource");
                    throw new DatabaseException(SystemErrors.ERROR_INIT);
                }
                _logger.info("Using datasource specified in engine");
            } else {
                _logger.error("Could not create a valid datasource");
                throw new DatabaseException(SystemErrors.ERROR_INIT);
            }
        }

        //Read query configuration
        createQueries(_eConfig);

        verifyTableConfig(_eConfig);

        if (_tCleaner != null)
            _tCleaner.start();
    }

    /**
     * Create a new session if max is not reached yet.
     * @throws SessionException if maximum is reached.
     * @see com.alfaariss.oa.engine.core.session.factory.ISessionFactory#createSession(java.lang.String)
     */
    public ISession createSession(String sRequestorId) throws SessionException {
        if (sRequestorId == null)
            throw new IllegalArgumentException("Suplied requestor id is empty");
        if (_lMax > 0) {
            if (getSessionCount() >= _lMax) {
                _logger.error("Could not create session, maximum reached");
                throw new SessionException(SystemErrors.ERROR_SESSION_MAX);
            }
        }
        return new JDBCSession(this, sRequestorId);
    }

    /**
    * Check if a Session with the given ID exists.   
    * @see IEntityManager#exists(java.lang.Object)
    */
    public boolean exists(Object id) throws PersistenceException {
        if (id == null || !(id instanceof String))
            throw new IllegalArgumentException("Suplied id is empty or invalid");

        boolean bRet = false;
        Connection oConnection = null;
        PreparedStatement psSelect = null;
        ResultSet rsSelect = null;

        try {
            oConnection = _oDataSource.getConnection();
            psSelect = oConnection.prepareStatement(_sSearchQuery);
            psSelect.setString(1, (String) id);
            rsSelect = psSelect.executeQuery();
            if (rsSelect.next())
                bRet = true;
        } catch (SQLException e) {
            _logger.error("Could not execute search query", e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_RETRIEVE);
        } catch (Exception e) {
            _logger.error("Internal error during exists check for session with id: " + id, e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_RETRIEVE);
        } finally {
            try {
                if (rsSelect != null)
                    rsSelect.close();
            } catch (SQLException e) {
                _logger.debug("Could not close resultset", e);
            }
            try {
                if (psSelect != null)
                    psSelect.close();
            } catch (SQLException e) {
                _logger.debug("Could not close statement", e);
            }
            try {
                if (oConnection != null)
                    oConnection.close();
            } catch (SQLException e) {
                _logger.debug("Could not close connection", e);
            }
        }
        return bRet;
    }

    /**
     * Retrieve the Session with the given id.
     * @param id The Session id.
     * @return The Session, or null if a Session with the given id does not exist.
     * @throws PersistenceException If retrieving fails.
     */
    @SuppressWarnings("unchecked") //Serialize value can not be checked
    public JDBCSession retrieve(Object id) throws PersistenceException {
        if (id == null || !(id instanceof String))
            throw new IllegalArgumentException("Suplied id is empty or invalid");

        Connection oConnection = null;
        JDBCSession session = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            oConnection = _oDataSource.getConnection();
            ps = oConnection.prepareStatement(_sSearchQuery);
            ps.setString(1, (String) id);
            rs = ps.executeQuery();
            if (rs.next()) {
                session = new JDBCSession(this, rs.getString(_sColumnREQUESTOR));
                session.setId((String) id);

                String sTGTID = rs.getString(_sColumnTGT_ID);
                if (sTGTID != null)
                    session.setTGTId(sTGTID);

                session.setState(SessionState.values()[rs.getInt(_sColumnSTATE)]);

                String sUrl = rs.getString(_sColumnURL);
                if (sUrl != null)
                    session.setProfileURL(sUrl);

                IUser oUser = (IUser) Serialize.decode(rs.getBytes(_sColumnOWNER));
                if (oUser != null)
                    session.setUser(oUser);

                session.setExpTime(rs.getTimestamp(_sColumnEXPIRATION).getTime());
                session.setForcedAuthentication(rs.getBoolean(_sColumnFORCED_AUTHENTICATE));
                session.setPassive(rs.getBoolean(_sColumnPASSIVE));

                SessionAttributes oAttributes = (SessionAttributes) Serialize
                        .decode(rs.getBytes(_sColumnATTRIBUTES));
                if (oAttributes != null)
                    session.setAttributes(oAttributes);

                String sForcedUid = rs.getString(_sColumnFORCED_USERID);
                if (sForcedUid != null)
                    session.setForcedUserID(sForcedUid);

                Locale oLocale = (Locale) Serialize.decode(rs.getBytes(_sColumnLOCALE));
                if (oLocale != null)
                    session.setLocale(oLocale);

                List listProfiles = (List) Serialize.decode(rs.getBytes(_sColumnAUTHN_PROFILES));
                if (listProfiles != null)
                    session.setAuthNProfiles(listProfiles);

                AuthenticationProfile oProfile = (AuthenticationProfile) Serialize
                        .decode(rs.getBytes(_sColumnSELECTED_AUTHN_PROFILE));
                if (oProfile != null)
                    session.setSelectedAuthNProfile(oProfile);
            }
        } catch (SQLException e) {
            _logger.error("Could not execute search query: " + _sSearchQuery, e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_RETRIEVE);
        } catch (ClassCastException e) {
            _logger.error("Could not decode, invalid class type", e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_RETRIEVE);
        } catch (Exception e) {
            _logger.error("Internal error during retrieve of session id: " + id, e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_RETRIEVE);
        } finally {
            try {
                if (rs != null)
                    rs.close();
            } catch (SQLException e) {
                _logger.debug("Could not close resultset", e);
            }
            try {
                if (ps != null)
                    ps.close();
            } catch (SQLException e) {
                _logger.debug("Could not close statement", e);
            }
            try {
                if (oConnection != null)
                    oConnection.close();
            } catch (SQLException e) {
                _logger.debug("Could not close connection", e);
            }
        }
        return session;
    }

    /**
     * Persist the session in the JDBC storage.
     * 
     * The sessionstate is saved with its name.
     * @param session The session to persist.
     * @throws PersistenceException If persistance fails.
     * @see IEntityManager#persist(IEntity)
     */
    public void persist(JDBCSession session) throws PersistenceException {
        if (session == null)
            throw new IllegalArgumentException("Suplied session is empty or invalid");

        Connection oConnection = null;
        PreparedStatement psInsert = null;
        PreparedStatement psDelete = null;
        PreparedStatement psUpdate = null;

        String id = session.getId();
        try {
            oConnection = _oDataSource.getConnection();
            if (id == null) // New Session
            {
                try {
                    byte[] baId = new byte[ISession.ID_BYTE_LENGTH];
                    do {
                        _random.nextBytes(baId);
                        try {
                            id = ModifiedBase64.encode(baId);
                        } catch (UnsupportedEncodingException e) {
                            _logger.error("Could not create id for byte[]: " + baId, e);
                            throw new PersistenceException(SystemErrors.ERROR_INTERNAL);
                        }
                    } while (exists(id)); //Key allready exists  

                    // Update expiration time and id
                    long expiration = System.currentTimeMillis() + _lExpiration;
                    session.setTgtExpTime(expiration);
                    session.setId(id);

                    //Create statement                  
                    psInsert = oConnection.prepareStatement(_sInsertQuery);
                    psInsert.setString(1, id);
                    psInsert.setString(2, session.getTGTId());
                    psInsert.setInt(3, session.getState().ordinal());
                    psInsert.setString(4, session.getRequestorId());
                    psInsert.setString(5, session.getProfileURL());
                    psInsert.setBytes(6, Serialize.encode(session.getUser()));
                    psInsert.setTimestamp(7, new Timestamp(expiration));
                    psInsert.setBoolean(8, session.isForcedAuthentication());
                    psInsert.setBoolean(9, session.isPassive());
                    psInsert.setBytes(10, Serialize.encode(session.getAttributes()));
                    psInsert.setString(11, session.getForcedUserID());
                    psInsert.setBytes(12, Serialize.encode(session.getLocale()));
                    psInsert.setBytes(13, Serialize.encode(session.getSelectedAuthNProfile()));
                    psInsert.setBytes(14, Serialize.encode(session.getAuthNProfiles()));

                    int i = psInsert.executeUpdate();
                    _logger.info(i + " new session(s) added: " + id + " for requestor '" + session.getRequestorId()
                            + "'");
                } catch (SQLException e) {
                    _logger.error("Could not execute insert query: " + _sInsertQuery, e);
                    throw new PersistenceException(SystemErrors.ERROR_RESOURCE_INSERT);
                }

            } else if (session.isExpired()) // Expired
            {
                try {
                    _logger.info("Session Expired: " + id);

                    _eventLogger.info(new UserEventLogItem(session, null, UserEvent.SESSION_EXPIRED, this, null));

                    psDelete = oConnection.prepareStatement(_sRemoveQuery);
                    psDelete.setString(1, id);
                    int i = psDelete.executeUpdate();
                    _logger.debug(i + " session(s) removed: " + id);
                } catch (SQLException e) {
                    _logger.error("Could not execute delete query: " + _sRemoveQuery, e);
                    throw new PersistenceException(SystemErrors.ERROR_RESOURCE_REMOVE);
                }
            } else // Update
            {
                try {
                    // Update expiration time
                    long expiration = System.currentTimeMillis() + _lExpiration;
                    session.setExpTime(expiration);
                    psUpdate = oConnection.prepareStatement(_sUpdateQuery);
                    psUpdate.setString(1, session.getTGTId());
                    psUpdate.setInt(2, session.getState().ordinal());
                    psUpdate.setString(3, session.getRequestorId());
                    psUpdate.setString(4, session.getProfileURL());
                    psUpdate.setBytes(5, Serialize.encode(session.getUser()));
                    psUpdate.setTimestamp(6, new Timestamp(expiration));
                    psUpdate.setBoolean(7, session.isForcedAuthentication());
                    psUpdate.setBoolean(8, session.isPassive());
                    psUpdate.setBytes(9, Serialize.encode(session.getAttributes()));
                    psUpdate.setString(10, session.getForcedUserID());
                    psUpdate.setBytes(11, Serialize.encode(session.getLocale()));
                    psUpdate.setBytes(12, Serialize.encode(session.getSelectedAuthNProfile()));
                    psUpdate.setBytes(13, Serialize.encode(session.getAuthNProfiles()));
                    psUpdate.setString(14, id);

                    int i = psUpdate.executeUpdate();
                    _logger.info(
                            i + " session(s) updated: " + id + " for requestor '" + session.getRequestorId() + "'");
                } catch (SQLException e) {
                    _logger.error("Could not execute update query: " + _sUpdateQuery, e);
                    throw new PersistenceException(SystemErrors.ERROR_RESOURCE_UPDATE);
                }
            }
        } catch (PersistenceException e) {
            throw e;
        } catch (Exception e) {
            _logger.error("Internal error during persist of session with id: " + id, e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_UPDATE);
        } finally {
            try {
                if (psInsert != null)
                    psInsert.close();
            } catch (SQLException e) {
                _logger.debug("Could not close insert statement", e);
            }
            try {
                if (psDelete != null)
                    psDelete.close();
            } catch (SQLException e) {
                _logger.debug("Could not close delete statement", e);
            }
            try {
                if (psUpdate != null)
                    psUpdate.close();
            } catch (SQLException e) {
                _logger.debug("Could not close update statement", e);
            }
            try {
                if (oConnection != null)
                    oConnection.close();
            } catch (SQLException e) {
                _logger.debug("Could not close connection", e);
            }
        }
    }

    /**
     * Uses a batch update to persist all supplied sessions.
     * @param sessions The sessions to persist.
     * @throws PersistenceException If persistance fails.
     * 
     * @see IEntityManager#persist(IEntity[])
     * @see PreparedStatement#addBatch()
     */
    public void persist(JDBCSession[] sessions) throws PersistenceException {
        if (sessions == null)
            throw new IllegalArgumentException("Suplied session array is empty or invalid");

        Connection connection = null;
        PreparedStatement psInsert = null;
        PreparedStatement psDelete = null;
        PreparedStatement psUpdate = null;
        try {
            connection = _oDataSource.getConnection(); //Manage connection
            connection.setAutoCommit(false);

            psInsert = connection.prepareStatement(_sInsertQuery);
            psDelete = connection.prepareStatement(_sRemoveQuery);
            psUpdate = connection.prepareStatement(_sUpdateQuery);

            for (JDBCSession session : sessions) {
                String id = session.getId();
                if (id == null) {
                    byte[] baId = new byte[ISession.ID_BYTE_LENGTH];
                    do {
                        _random.nextBytes(baId);
                        try {
                            id = ModifiedBase64.encode(baId);
                        } catch (UnsupportedEncodingException e) {
                            _logger.error("Could not create id for byte[]: " + baId, e);
                            throw new PersistenceException(SystemErrors.ERROR_INTERNAL);
                        }
                    } while (exists(id)); //Key allready exists   

                    session.setId(id);
                    //Update expiration time
                    long expiration = System.currentTimeMillis() + _lExpiration;
                    session.setTgtExpTime(expiration);
                    psInsert.setString(1, id);
                    psInsert.setString(2, session.getTGTId());
                    psInsert.setInt(3, session.getState().ordinal());
                    psInsert.setString(4, session.getRequestorId());
                    psInsert.setString(5, session.getProfileURL());
                    psInsert.setBytes(6, Serialize.encode(session.getUser()));
                    psInsert.setTimestamp(7, new Timestamp(expiration));
                    psInsert.setBoolean(8, session.isForcedAuthentication());
                    psInsert.setBoolean(9, session.isPassive());
                    psInsert.setBytes(10, Serialize.encode(session.getAttributes()));
                    psInsert.setString(11, session.getForcedUserID());
                    psInsert.setBytes(12, Serialize.encode(session.getLocale()));
                    psInsert.setBytes(13, Serialize.encode(session.getSelectedAuthNProfile()));
                    psInsert.setBytes(14, Serialize.encode(session.getAuthNProfiles()));
                    psInsert.addBatch();
                } else if (session.isExpired()) //Expired
                {
                    _logger.info("Session Expired: " + id);

                    _eventLogger.info(new UserEventLogItem(session, null, UserEvent.SESSION_EXPIRED, this, null));

                    psDelete.setString(1, id);
                    psDelete.addBatch();
                } else //Update
                {
                    //Update expiration time
                    long expiration = System.currentTimeMillis() + _lExpiration;
                    session.setTgtExpTime(expiration);
                    psUpdate.setString(1, session.getTGTId());
                    psUpdate.setInt(2, session.getState().ordinal());
                    psUpdate.setString(3, session.getRequestorId());
                    psUpdate.setString(4, session.getProfileURL());
                    psUpdate.setBytes(5, Serialize.encode(session.getUser()));
                    psUpdate.setTimestamp(6, new Timestamp(expiration));
                    psUpdate.setBoolean(7, session.isForcedAuthentication());
                    psInsert.setBoolean(8, session.isPassive());
                    psUpdate.setBytes(9, Serialize.encode(session.getAttributes()));
                    psUpdate.setString(10, session.getForcedUserID());
                    psUpdate.setBytes(11, Serialize.encode(session.getLocale()));
                    psUpdate.setBytes(12, Serialize.encode(session.getSelectedAuthNProfile()));
                    psUpdate.setBytes(13, Serialize.encode(session.getAuthNProfiles()));
                    psUpdate.setString(14, id);
                    psUpdate.addBatch();
                }
            }
            try {
                int[] iResult = psInsert.executeBatch();
                if (_logger.isDebugEnabled()) {
                    int iTotalAdded = 0;
                    for (int i : iResult)
                        iTotalAdded += i;

                    _logger.info(iTotalAdded + " new session(s) added by batch");
                }
            } catch (SQLException e) {
                _logger.error("Could not execute insert batch", e);
                throw new PersistenceException(SystemErrors.ERROR_RESOURCE_INSERT);
            }
            try {
                int[] iResult = psDelete.executeBatch();
                if (_logger.isDebugEnabled()) {
                    int iTotalDeleted = 0;
                    for (int i : iResult)
                        iTotalDeleted += i;

                    _logger.info(iTotalDeleted + " session(s) deleted by batch");
                }

            } catch (SQLException e) {
                _logger.error("Could not execute delete batch", e);
                throw new PersistenceException(SystemErrors.ERROR_RESOURCE_REMOVE);
            }
            try {
                int[] iResult = psUpdate.executeBatch();
                if (_logger.isDebugEnabled()) {
                    int iTotalUpdated = 0;
                    for (int i : iResult)
                        iTotalUpdated += i;

                    _logger.info(iTotalUpdated + " session(s) updated by batch");
                }
            } catch (SQLException e) {
                _logger.error("Could not execute update batch", e);
                throw new PersistenceException(SystemErrors.ERROR_RESOURCE_UPDATE);
            }

            connection.commit();
        } catch (SQLException e) {
            _logger.error("Could not execute batch", e);
            try {
                if (connection != null)
                    connection.rollback();
            } catch (SQLException e1) {
                _logger.warn("Could not rollback batch", e);
            }

            throw new PersistenceException(SystemErrors.ERROR_INTERNAL);
        } catch (PersistenceException e) {
            try {
                if (connection != null)
                    connection.rollback();
            } catch (SQLException e1) {
                _logger.warn("Could not rollback batch", e);
            }
            throw e;
        } catch (Exception e) {
            _logger.error("Internal error during session persist", e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_CONNECT);
        } finally {
            try {
                if (psInsert != null)
                    psInsert.close();
            } catch (SQLException e) {
                _logger.debug("Could not close insert statement", e);
            }
            try {
                if (psDelete != null)
                    psDelete.close();
            } catch (SQLException e) {
                _logger.debug("Could not close delete statement", e);
            }
            try {
                if (psUpdate != null)
                    psUpdate.close();
            } catch (SQLException e) {
                _logger.debug("Could not close update statement", e);
            }
            try {
                if (connection != null)
                    connection.close();
            } catch (SQLException e) {
                _logger.debug("Could not close connection", e);
            }
        }
    }

    /**
     * Remove all expired sessions.
     * 
     * e.g. <code>DELETE FROM session WHERE expiration <= NOW()</code>
     * @see ICleanable#removeExpired()
     */
    public void removeExpired() throws PersistenceException {
        //DD does not log a user event when session is expired, because this will cost an unnecesary select (eventlogging should be done with DB trigger)
        Connection oConnection = null;
        PreparedStatement ps = null;
        try {
            oConnection = _oDataSource.getConnection();
            ps = oConnection.prepareStatement(_sRemoveExpiredQuery);
            ps.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
            int i = ps.executeUpdate();
            if (i > 0)
                _logger.info(i + " session(s) expired");
        } catch (SQLException e) {
            _logger.error("Could not execute delete expired", e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_REMOVE);
        } catch (Exception e) {
            _logger.error("Internal error while delete expired sessions", e);
            throw new PersistenceException(SystemErrors.ERROR_RESOURCE_REMOVE);
        } finally {
            try {
                if (ps != null)
                    ps.close();
            } catch (SQLException e) {
                _logger.debug("Could not close statement", e);
            }
            try {
                if (oConnection != null)
                    oConnection.close();
            } catch (SQLException e) {
                _logger.debug("Could not close connection", e);
            }
        }
    }

    /**
     * @see com.alfaariss.oa.api.poll.IPollable#poll()
     */
    public long poll() throws OAException {
        return getSessionCount();
    }

    /**
     * @see com.alfaariss.oa.api.logging.IAuthority#getAuthority()
     * @since 1.3
     */
    public String getAuthority() {
        return ISessionFactory.AUTHORITY_NAME;
    }

    /*
     * Read the entity storage configuration. 
     * 
     * If no configuration section is present the default queries are used.
     */
    private void createQueries(Element eConfig) throws SessionException, ConfigurationException {
        Element eEntity = _configurationManager.getSection(eConfig, "entity");
        if (eEntity == null) {
            _logger.info(
                    "No optional 'entity' section found in configuration, using default table and column names");
        } else {
            _sTableName = _configurationManager.getParam(eEntity, "table");
            _sColumnID = getIdColumnName(eEntity);
            _sColumnTGT_ID = getColumnName(eEntity, "tgtId");
            _sColumnSTATE = getColumnName(eEntity, "state");
            _sColumnREQUESTOR = getColumnName(eEntity, "requestorId");
            _sColumnURL = getColumnName(eEntity, "profileURL");
            _sColumnEXPIRATION = getColumnName(eEntity, "expTime");
            _sColumnFORCED_AUTHENTICATE = getColumnName(eEntity, "forcedAuthentication");
            _sColumnOWNER = getColumnName(eEntity, "user");
            _sColumnATTRIBUTES = getColumnName(eEntity, "attributes");
            _sColumnFORCED_USERID = getColumnName(eEntity, "forcedUserId");
            _sColumnLOCALE = getColumnName(eEntity, "locale");
            _sColumnSELECTED_AUTHN_PROFILE = getColumnName(eEntity, "selectedAuthNProfile");
            _sColumnAUTHN_PROFILES = getColumnName(eEntity, "authenticationProfiles");
            _sColumnPASSIVE = getColumnName(eEntity, "passive");
        }

        if (_sTableName == null)
            _sTableName = TABLE_NAME;

        if (_sColumnID == null)
            _sColumnID = COLUMN_ID;

        if (_sColumnTGT_ID == null)
            _sColumnTGT_ID = COLUMN_TGT_ID;

        if (_sColumnSTATE == null)
            _sColumnSTATE = COLUMN_STATE;

        if (_sColumnREQUESTOR == null)
            _sColumnREQUESTOR = COLUMN_REQUESTOR;

        if (_sColumnURL == null)
            _sColumnURL = COLUMN_URL;

        if (_sColumnEXPIRATION == null)
            _sColumnEXPIRATION = COLUMN_EXPIRATION;

        if (_sColumnFORCED_AUTHENTICATE == null)
            _sColumnFORCED_AUTHENTICATE = COLUMN_FORCED_AUTHENTICATE;

        if (_sColumnPASSIVE == null)
            _sColumnPASSIVE = COLUMN_PASSIVE;

        if (_sColumnOWNER == null)
            _sColumnOWNER = COLUMN_OWNER;

        if (_sColumnATTRIBUTES == null)
            _sColumnATTRIBUTES = COLUMN_ATTRIBUTES;

        if (_sColumnFORCED_USERID == null)
            _sColumnFORCED_USERID = COLUMN_FORCED_USERID;

        if (_sColumnLOCALE == null)
            _sColumnLOCALE = COLUMN_LOCALE;

        if (_sColumnSELECTED_AUTHN_PROFILE == null)
            _sColumnSELECTED_AUTHN_PROFILE = COLUMN_SELECTED_AUTHN_PROFILE;

        if (_sColumnAUTHN_PROFILES == null)
            _sColumnAUTHN_PROFILES = COLUMN_AUTHN_PROFILES;

        //SearchQuery
        StringBuffer sb = new StringBuffer("SELECT ");
        sb.append(_sColumnTGT_ID).append(", ");
        sb.append(_sColumnSTATE).append(", ");
        sb.append(_sColumnREQUESTOR).append(", ");
        sb.append(_sColumnURL).append(", ");
        sb.append(_sColumnOWNER).append(", ");
        sb.append(_sColumnEXPIRATION).append(", ");
        sb.append(_sColumnFORCED_AUTHENTICATE).append(", ");
        sb.append(_sColumnPASSIVE).append(", ");
        sb.append(_sColumnATTRIBUTES).append(", ");
        sb.append(_sColumnFORCED_USERID).append(", ");
        sb.append(_sColumnLOCALE).append(", ");
        sb.append(_sColumnSELECTED_AUTHN_PROFILE).append(", ");
        sb.append(_sColumnAUTHN_PROFILES);
        sb.append(" FROM ").append(_sTableName);
        sb.append(" WHERE ").append(_sColumnID).append("=?");
        _sSearchQuery = sb.toString();
        _logger.debug("Using SearchQuery: " + _sSearchQuery);

        //CountQuery 
        sb = new StringBuffer("SELECT COUNT(");
        sb.append(_sColumnID).append(") FROM ").append(_sTableName);
        _sCountQuery = sb.toString();
        _logger.debug("Using CountQuery: " + _sCountQuery);

        //InsertQuery
        sb = new StringBuffer("INSERT INTO ");
        sb.append(_sTableName).append("(");
        sb.append(_sColumnID).append(",");
        sb.append(_sColumnTGT_ID).append(",");
        sb.append(_sColumnSTATE).append(",");
        sb.append(_sColumnREQUESTOR).append(",");
        sb.append(_sColumnURL).append(",");
        sb.append(_sColumnOWNER).append(",");
        sb.append(_sColumnEXPIRATION).append(",");
        sb.append(_sColumnFORCED_AUTHENTICATE).append(",");
        sb.append(_sColumnPASSIVE).append(",");
        sb.append(_sColumnATTRIBUTES).append(",");
        sb.append(_sColumnFORCED_USERID).append(",");
        sb.append(_sColumnLOCALE).append(",");
        sb.append(_sColumnSELECTED_AUTHN_PROFILE).append(",");
        sb.append(_sColumnAUTHN_PROFILES);
        sb.append(") VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
        _sInsertQuery = sb.toString();
        _logger.debug("Using InsertQuery: " + _sInsertQuery);

        //UpdateQuery
        sb = new StringBuffer("UPDATE ");
        sb.append(_sTableName).append(" SET ");
        sb.append(_sColumnTGT_ID).append("=?, ");
        sb.append(_sColumnSTATE).append("=?, ");
        sb.append(_sColumnREQUESTOR).append("=?, ");
        sb.append(_sColumnURL).append("=?, ");
        sb.append(_sColumnOWNER).append("=?, ");
        sb.append(_sColumnEXPIRATION).append("=?, ");
        sb.append(_sColumnFORCED_AUTHENTICATE).append("=?, ");
        sb.append(_sColumnPASSIVE).append("=?, ");
        sb.append(_sColumnATTRIBUTES).append("=?, ");
        sb.append(_sColumnFORCED_USERID).append("=?, ");
        sb.append(_sColumnLOCALE).append("=?, ");
        sb.append(_sColumnSELECTED_AUTHN_PROFILE).append("=?, ");
        sb.append(_sColumnAUTHN_PROFILES).append("=? WHERE  ");
        sb.append(_sColumnID).append("=?");
        _sUpdateQuery = sb.toString();
        _logger.debug("Using UpdateQuery: " + _sUpdateQuery);

        //RemoveQuery
        sb = new StringBuffer("DELETE FROM ");
        sb.append(_sTableName).append(" WHERE ");
        sb.append(_sColumnID).append("=?");
        _sRemoveQuery = sb.toString();
        _logger.debug("Using RemoveQuery: " + _sRemoveQuery);

        //RemoveExpiredQuery 
        sb = new StringBuffer("DELETE FROM ");
        sb.append(_sTableName).append(" WHERE ");
        sb.append(_sColumnEXPIRATION).append("<=?");
        _sRemoveExpiredQuery = sb.toString();
        _logger.debug("Using RemoveExpiredQuery: " + _sRemoveExpiredQuery);
    }

    private void verifyTableConfig(Element eConfig) throws OAException {
        Connection oConnection = null;
        PreparedStatement pVerify = null;
        try {
            try {
                oConnection = _oDataSource.getConnection();
            } catch (SQLException e) {
                _logger.error("Could not connect to resource", e);
                throw new DatabaseException(SystemErrors.ERROR_INIT);
            }

            String sVerificationQuery = null;
            try {
                //<validation query=""/>    
                Element eValidation = _configurationManager.getSection(eConfig, "validation");
                if (eValidation != null) {
                    sVerificationQuery = _configurationManager.getParam(eValidation, "query");
                    if (sVerificationQuery == null || sVerificationQuery.length() == 0) {
                        //DD Do not verify the table if empty query configured
                        _logger.warn("Empty validation query found, table structure is not validated");
                        //finally is executed before return
                        return;
                    }
                }

                if (sVerificationQuery == null) {
                    //DD Use default query if no validation.query parameter configured                     
                    StringBuffer sbVerificationQuery = new StringBuffer("SELECT ");
                    sbVerificationQuery.append(_sColumnID).append(",");
                    sbVerificationQuery.append(_sColumnTGT_ID).append(",");
                    sbVerificationQuery.append(_sColumnSTATE).append(",");
                    sbVerificationQuery.append(_sColumnREQUESTOR).append(",");
                    sbVerificationQuery.append(_sColumnURL).append(",");
                    sbVerificationQuery.append(_sColumnEXPIRATION).append(",");
                    sbVerificationQuery.append(_sColumnFORCED_AUTHENTICATE).append(",");
                    sbVerificationQuery.append(_sColumnPASSIVE).append(",");
                    sbVerificationQuery.append(_sColumnOWNER).append(",");
                    sbVerificationQuery.append(_sColumnATTRIBUTES).append(",");
                    sbVerificationQuery.append(_sColumnFORCED_USERID).append(",");
                    sbVerificationQuery.append(_sColumnLOCALE).append(",");
                    sbVerificationQuery.append(_sColumnSELECTED_AUTHN_PROFILE).append(",");
                    sbVerificationQuery.append(_sColumnAUTHN_PROFILES);
                    sbVerificationQuery.append(" FROM ");
                    sbVerificationQuery.append(_sTableName);
                    sbVerificationQuery.append(" LIMIT 1");
                    sVerificationQuery = sbVerificationQuery.toString();
                    _logger.info("No validation query found, using default: " + sVerificationQuery);
                }
            } catch (ConfigurationException e) {

                _logger.error("Invalid validation query found", e);
                throw new DatabaseException(SystemErrors.ERROR_CONFIG_READ);
            }

            //Verify the table             
            pVerify = oConnection.prepareStatement(sVerificationQuery);
            try {
                pVerify.executeQuery();
            } catch (Exception e) {
                StringBuffer sbError = new StringBuffer("Invalid table configured '");
                sbError.append(_sTableName);
                sbError.append("' verified with query: ");
                sbError.append(sVerificationQuery);
                _logger.error(sbError.toString(), e);
                throw new DatabaseException(SystemErrors.ERROR_INIT);
            }
        } catch (OAException e) {
            throw e;
        } catch (Exception e) {
            _logger.error("Internal error during verification of configured table", e);
            throw new OAException(SystemErrors.ERROR_INIT);
        } finally {
            try {
                if (pVerify != null)
                    pVerify.close();
            } catch (SQLException e) {
                _logger.debug("Could not close statement", e);
            }
            try {
                if (oConnection != null)
                    oConnection.close();
            } catch (SQLException e) {
                _logger.debug("Could not close connection", e);
            }
        }
    }

    //Retrieve id column name
    private String getIdColumnName(Element eConfig) throws SessionException, ConfigurationException {
        String sIdColumn = null;
        if (eConfig != null) {
            Element eIdColumn = _configurationManager.getSection(eConfig, "id");
            if (eIdColumn == null)
                _logger.error("No optional 'id' section found");
            else {
                sIdColumn = _configurationManager.getParam(eIdColumn, "column");
                if (sIdColumn == null) {
                    _logger.error("Could not find column name for id");
                    throw new SessionException(SystemErrors.ERROR_CONFIG_READ);
                }
            }
        }
        return sIdColumn;
    }

    //Retrieve property column name
    private String getColumnName(Element eConfig, String sName) throws SessionException, ConfigurationException {
        String sColumn = null;
        if (eConfig != null || sName != null) {
            Element eColumn = _configurationManager.getSection(eConfig, "property", "name=" + sName);
            if (eColumn == null)
                _logger.warn("No optional 'property' section found for property with name: " + sName);
            else {
                sColumn = _configurationManager.getParam(eColumn, "column");
                if (sColumn == null) {
                    _logger.error("Could not find column name for property " + sName);
                    throw new SessionException(SystemErrors.ERROR_CONFIG_READ);
                }
            }
        }
        return sColumn;
    }

    //Retrieve the current number of sessions
    private int getSessionCount() throws SessionException {
        Connection oConnection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        int iRet = 0;
        try {
            oConnection = _oDataSource.getConnection();
            ps = oConnection.prepareStatement(_sCountQuery);
            rs = ps.executeQuery();
            if (rs.next())
                iRet = rs.getInt(1);
        } catch (SQLException e) {
            _logger.error("Could not execute count query", e);
            throw new SessionException(SystemErrors.ERROR_RESOURCE_RETRIEVE);
        } catch (Exception e) {
            _logger.error("Internal error during session count retrieval", e);
            throw new SessionException(SystemErrors.ERROR_RESOURCE_RETRIEVE);
        } finally {
            try {
                if (rs != null)
                    rs.close();
            } catch (SQLException e) {
                _logger.debug("Could not close resultset", e);
            }
            try {
                if (ps != null)
                    ps.close();
            } catch (SQLException e) {
                _logger.debug("Could not close statement", e);
            }
            try {
                if (oConnection != null)
                    oConnection.close();
            } catch (SQLException e) {
                _logger.debug("Could not close connection", e);
            }
        }
        return iRet;
    }
}