public class Constants {
    /**
     * Default timezone ID.
     */
    public static final String DEFAULT_TIMEZONE_ID = "Europe/London";
    
    /**
     * Administrator's default password ("admin").
     */
    public static final String DEFAULT_ADMIN_PASSWORD = "$2a$05$6Ny3TjrW3aVAL1or2SlcR.fhuDgPKp5jp.P9fBXwVNePgeLqb4i3C";

    /**
     * Default generic user role.
     */
    public static final String DEFAULT_USER_ROLE = "user";
} public class AuthenticationTokenDao {
    /**
     * Gets an authentication token.
     * 
     * @param id Authentication token ID
     * @return Authentication token
     */
    public AuthenticationToken get(String id) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        return handle.createQuery("select AUT_ID_C, AUT_IDUSER_C, AUT_LONGLASTED_B, AUT_CREATEDATE_D, AUT_LASTCONNECTIONDATE_D" +
                "  from T_AUTHENTICATION_TOKEN" +
                "  where AUT_ID_C = :id")
                .bind("id", id)
                .mapTo(AuthenticationToken.class)
                .first();
    }

    /**
     * Creates a new authentication token.
     * 
     * @param authenticationToken Authentication token
     * @return Authentication token ID
     */
    public String create(AuthenticationToken authenticationToken) {
        authenticationToken.setId(UUID.randomUUID().toString());
        authenticationToken.setCreateDate(new Date());

        final Handle handle = ThreadLocalContext.get().getHandle();
        handle.createStatement("insert into " +
                "  T_AUTHENTICATION_TOKEN(AUT_ID_C, AUT_IDUSER_C, AUT_LONGLASTED_B, AUT_CREATEDATE_D)" +
                "  values(:id, :userId, :longLasted, :createDate)")
                .bind("id", authenticationToken.getId())
                .bind("userId", authenticationToken.getUserId())
                .bind("longLasted", authenticationToken.isLongLasted())
                .bind("createDate", authenticationToken.getCreateDate())
                .execute();
            
        return authenticationToken.getId();
    }

    /**
     * Deletes the authentication token.
     * 
     * @param authenticationTokenId Authentication token ID
     * @throws Exception
     */
    public void delete(String authenticationTokenId) throws Exception {
        final Handle handle = ThreadLocalContext.get().getHandle();
        AuthenticationToken authenticationToken = get(authenticationTokenId);
        if (authenticationToken != null) {
            handle.createStatement("delete from " +
                    "  T_AUTHENTICATION_TOKEN" +
                    "  where AUT_ID_C = :id")
                    .bind("id", authenticationToken.getId())
                    .execute();
        } else {
            throw new Exception("Token not found: " + authenticationTokenId);
        }
    }

    /**
     * Deletes old short lived tokens.
     *
     * @param userId User ID
     */
    public void deleteOldSessionToken(String userId) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        handle.createStatement("delete from T_AUTHENTICATION_TOKEN AS ato " +
                "  where ato.AUT_IDUSER_C = :userId and ato.AUT_LONGLASTED_B = :longLasted" +
                "  and ato.AUT_LASTCONNECTIONDATE_D < :minDate")
                .bind("userId", userId)
                .bind("longLasted", false)
                .bind("minDate", DateTime.now().minusDays(1).toDate())
                .execute();
    }

    /**
     * Deletes old short lived tokens.
     *
     * @param id Token id
     */
    public void updateLastConnectionDate(String id) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        handle.createStatement("update T_AUTHENTICATION_TOKEN ato " +
                "  set ato.AUT_LASTCONNECTIONDATE_D = :currentDate" +
                "  where ato.AUT_ID_C = :id ")
                .bind("currentDate", new Date())
                .bind("id", id)
                .execute();
    }
} public class RoleBaseFunctionDao {
    /**
     * Find the set of base functions of a role.
     * 
     * @param roleId Role ID
     * @return Set of base functions
     */
    public Set<String> findByRoleId(String roleId) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        List<Map<String, Object>> resultList = handle.createQuery("select rbf.RBF_IDBASEFUNCTION_C " +
                "  from T_ROLE_BASE_FUNCTION rbf, T_ROLE r" +
                "  where rbf.RBF_IDROLE_C = :roleId and rbf.RBF_DELETEDATE_D is null" +
                "  and r.ROL_ID_C = rbf.RBF_IDROLE_C and r.ROL_DELETEDATE_D is null")
                .bind("roleId", roleId)
                .list();
        Set<String> roleSet = Sets.newHashSet();
        for (Map<String, Object> role : resultList) {
            roleSet.add((String) role.get("RBF_IDBASEFUNCTION_C"));
        }
        return roleSet;
    }
} public class UserDao {
    /**
     * Authenticates an user.
     * 
     * @param username User login
     * @param password User password
     * @return ID of the authenticated user or null
     */
    public String authenticate(String username, String password) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        Query<User> q = handle.createQuery("select " + new UserMapper().getJoinedColumns("u") +
                "  from T_USER u" +
                "  where u.USE_USERNAME_C = :username and u.USE_DELETEDATE_D is null")
                .bind("username", username)
                .mapTo(User.class);
        User user = q.first();
        if (user == null || !BCrypt.checkpw(password, user.getPassword())) {
            return null;
        }
        return user.getId();
    }
    
    /**
     * Creates a new user.
     * 
     * @param user User to create
     * @return User ID
     * @throws Exception
     */
    public String create(User user) throws Exception {
        // Init user data
        user.setId(UUID.randomUUID().toString());
        user.setPassword(hashPassword(user.getPassword()));
        user.setCreateDate(new Date());

        // Checks for user unicity
        if (getActiveByUsername(user.getUsername()) != null) {
            throw new Exception("AlreadyExistingUsername");
        }

        // Create user
        final Handle handle = ThreadLocalContext.get().getHandle();
        handle.createStatement("insert into " +
                " T_USER(USE_ID_C, USE_IDROLE_C, USE_USERNAME_C, USE_PASSWORD_C, USE_EMAIL_C, USE_FIRSTCONNECTION_B, USE_CREATEDATE_D)" +
                " values(:id, :roleId, :username, :password, :email, :firstConnection, :createDate)")
                .bind("id", user.getId())
                .bind("roleId", user.getRoleId())
                .bind("username", user.getUsername())
                .bind("password", user.getPassword())
                .bind("email", user.getEmail())
                .bind("firstConnection", user.isFirstConnection())
                .bind("createDate", user.getCreateDate())
                .execute();

        return user.getId();
    }
    
    /**
     * Updates a user.
     * 
     * @param user User to update
     * @return Updated user
     */
    public User update(User user) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        handle.createStatement("update T_USER u set " +
                " u.USE_EMAIL_C = :email, " +
                " u.USE_FIRSTCONNECTION_B = :firstConnection " +
                " where u.USE_ID_C = :id and u.USE_DELETEDATE_D is null")
                .bind("id", user.getId())
                .bind("email", user.getEmail())
                .bind("firstConnection", user.isFirstConnection())
                .execute();

        return user;
    }
    
    /**
     * Update the user password.
     * 
     * @param user User to update
     * @return Updated user
     */
    public User updatePassword(User user) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        handle.createStatement("update T_USER u set " +
                " u.USE_PASSWORD_C = :password " +
                " where u.USE_ID_C = :id and u.USE_DELETEDATE_D is null")
                .bind("id", user.getId())
                .bind("password", hashPassword(user.getPassword()))
                .execute();

        return user;
    }

    /**
     * Gets a user by its ID.
     * 
     * @param id User ID
     * @return User
     */
    public User getActiveById(String id) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        Query<User> q = handle.createQuery("select " + new UserMapper().getJoinedColumns("u") +
                "  from T_USER u" +
                "  where u.USE_ID_C = :id and u.USE_DELETEDATE_D is null")
                .bind("id", id)
                .mapTo(User.class);
        return q.first();
    }
    
    /**
     * Gets an active user by its username.
     * 
     * @param username User's username
     * @return User
     */
    public User getActiveByUsername(String username) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        Query<User> q = handle.createQuery("select " + new UserMapper().getJoinedColumns("u") +
                "  from T_USER u" +
                "  where u.USE_USERNAME_C = :username and u.USE_DELETEDATE_D is null")
                .bind("username", username)
                .mapTo(User.class);
        return q.first();
    }
    
    /**
     * Gets an active user by its password recovery token.
     * 
     * @param passwordResetKey Password recovery token
     * @return User
     */
    public User getActiveByPasswordResetKey(String passwordResetKey) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        Query<User> q = handle.createQuery("select " + new UserMapper().getJoinedColumns("u") +
                "  from T_USER u" +
                "  where u.USE_PASSWORDRESETKEY_C = :passwordResetKey and u.USE_DELETEDATE_D is null")
                .bind("passwordResetKey", passwordResetKey)
                .mapTo(User.class);
        return q.first();
    }
    
    /**
     * Deletes a user.
     * 
     * @param username User's username
     */
    public void delete(String username) {
        final Handle handle = ThreadLocalContext.get().getHandle();
        handle.createStatement("update T_USER u" +
                "  set u.USE_DELETEDATE_D = :deleteDate" +
                "  where u.USE_USERNAME_C = :username and u.USE_DELETEDATE_D is null")
                .bind("username", username)
                .bind("deleteDate", new Date())
                .execute();
    }

    /**
     * Hash the user's password.
     * 
     * @param password Clear password
     * @return Hashed password
     */
    protected String hashPassword(String password) {
        return BCrypt.hashpw(password, BCrypt.gensalt());
    }

    /**
     * Searches users by criteria.
     *
     * @param paginatedList Paginated list (populated by side effects)
     * @param criteria Search criteria
     * @param sortCriteria Sort criteria
     */
    public void findByCriteria(PaginatedList<UserDto> paginatedList, UserCriteria criteria, SortCriteria sortCriteria) {
        QueryParam queryParam = getQueryParam(criteria);
        List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria, true);
        List<UserDto> userDtoList = assembleResultList(l);
        paginatedList.setResultList(userDtoList);
    }

    /**
     * Searches users by criteria.
     *
     * @param criteria Search criteria
     * @return List of users
     */
    public List<UserDto> findByCriteria(UserCriteria criteria) {
        QueryParam queryParam = getQueryParam(criteria);
        Query<Map<String, Object>> q = QueryUtil.getNativeQuery(queryParam);
        List<Object[]> l = q.map(ColumnIndexMapper.INSTANCE).list();
        return assembleResultList(l);
    }

    /**
     * Creates the query parameters from the criteria.
     *
     * @param criteria Search criteria
     * @return Query parameters
     */
    private QueryParam getQueryParam(UserCriteria criteria) {
        Map<String, Object> parameterMap = new HashMap<String, Object>();
        StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3");
        sb.append(" from T_USER u ");

        // Add search criterias
        List<String> criteriaList = new ArrayList<String>();
        criteriaList.add("u.USE_DELETEDATE_D is null");

        if (!criteriaList.isEmpty()) {
            sb.append(" where ");
            sb.append(Joiner.on(" and ").join(criteriaList));
        }

        QueryParam queryParam = new QueryParam(sb.toString(), parameterMap);
        return queryParam;
    }

    /**
     * Assemble the query results.
     *
     * @param l Query results as a table
     * @return Query results as a list of domain objects
     */
    private List<UserDto> assembleResultList(List<Object[]> l) {
        List<UserDto> userDtoList = new ArrayList<UserDto>();
        for (Object[] o : l) {
            int i = 0;
            UserDto userDto = new UserDto();
            userDto.setId((String) o[i++]);
            userDto.setUsername((String) o[i++]);
            userDto.setEmail((String) o[i++]);
            userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
            userDtoList.add(userDto);
        }
        return userDtoList;
    }
} public class UserCriteria {
} public class UserDto {
    /**
     * User ID.
     */
    private String id;
    
    /**
     * Username.
     */
    private String username;
    
    /**
     * Email address.
     */
    private String email;
    
    /**
     * Creation date of this user.
     */
    private Long createTimestamp;

    /**
     * Getter of id.
     *
     * @return id
     */
    public String getId() {
        return id;
    }

    /**
     * Setter of id.
     *
     * @param id id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * Getter of username.
     *
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * Setter of username.
     *
     * @param username username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * Getter of email.
     *
     * @return email
     */
    public String getEmail() {
        return email;
    }

    /**
     * Setter of email.
     *
     * @param email email
     */
    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * Getter of createTimestamp.
     *
     * @return createTimestamp
     */
    public Long getCreateTimestamp() {
        return createTimestamp;
    }

    /**
     * Setter of createTimestamp.
     *
     * @param createTimestamp createTimestamp
     */
    public void setCreateTimestamp(Long createTimestamp) {
        this.createTimestamp = createTimestamp;
    }
} public class PasswordChangedEvent {
    /**
     * Created user.
     */
    private User user;

    /**
     * Getter of user.
     *
     * @return user
     */
    public User getUser() {
        return user;
    }

    /**
     * Setter of user.
     *
     * @param user user
     */
    public void setUser(User user) {
        this.user = user;
    }
} public class UserCreatedEvent {
    /**
     * Created user.
     */
    private User user;

    /**
     * Getter of user.
     *
     * @return user
     */
    public User getUser() {
        return user;
    }

    /**
     * Setter of user.
     *
     * @param user user
     */
    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("user", user)
                .toString();
    }
} public class AppContext {
    /**
     * Singleton instance.
     */
    private static AppContext instance;

    /**
     * Event bus.
     */
    private EventBus eventBus;
    
    /**
     * Generic asynchronous event bus.
     */
    private EventBus asyncEventBus;

    /**
     * Sensor service.
     */
    private SensorService sensorService;
    
    /**
     * Asynchronous executors.
     */
    private List<ExecutorService> asyncExecutorList;
    
    /**
     * Private constructor.
     */
    private AppContext() {
        resetEventBus();
        
        sensorService = new SensorService();
        sensorService.startAsync();
    }
    
    /**
     * (Re)-initializes the event buses.
     */
    private void resetEventBus() {
        eventBus = new EventBus();
        eventBus.register(new DeadEventListener());
        
        asyncExecutorList = new ArrayList<ExecutorService>();
        
        asyncEventBus = newAsyncEventBus();
    }

    /**
     * Returns a single instance of the application context.
     * 
     * @return Application context
     */
    public static AppContext getInstance() {
        if (instance == null) {
            instance = new AppContext();
        }
        return instance;
    }
    
    /**
     * Creates a new asynchronous event bus.
     * 
     * @return Async event bus
     */
    private EventBus newAsyncEventBus() {
        if (EnvironmentUtil.isUnitTest()) {
            return new EventBus();
        } else {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
                    0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>());
            asyncExecutorList.add(executor);
            return new AsyncEventBus(executor);
        }
    }

    /**
     * Getter of eventBus.
     *
     * @return eventBus
     */
    public EventBus getEventBus() {
        return eventBus;
    }

    /**
     * Getter of asyncEventBus.
     *
     * @return asyncEventBus
     */
    public EventBus getAsyncEventBus() {
        return asyncEventBus;
    }

    /**
     * Getter of sensorService.
     *
     * @return the sensorService
     */
    public SensorService getSensorService() {
        return sensorService;
    }
} public class AuthenticationToken {
    /**
     * Token.
     */
    private String id;

    /**
     * User ID.
     */
    private String userId;
    
    /**
     * Remember the user next time (long lasted session).
     */
    private boolean longLasted;
    
    /**
     * Token creation date.
     */
    private Date createDate;

    /**
     * Last connection date using this token.
     */
    private Date lastConnectionDate;

    public AuthenticationToken() {
    }

    public AuthenticationToken(String id, String userId, boolean longLasted, Date createDate, Date lastConnectionDate) {
        this.id = id;
        this.userId = userId;
        this.longLasted = longLasted;
        this.createDate = createDate;
        this.lastConnectionDate = lastConnectionDate;
    }

    /**
     * Getter of id.
     *
     * @return id
     */
    public String getId() {
        return id;
    }

    /**
     * Setter of id.
     *
     * @param id id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * Getter of userId.
     *
     * @return userId
     */
    public String getUserId() {
        return userId;
    }

    /**
     * Setter of userId.
     *
     * @param userId userId
     */
    public void setUserId(String userId) {
        this.userId = userId;
    }

    /**
     * Getter of longLasted.
     *
     * @return longLasted
     */
    public boolean isLongLasted() {
        return longLasted;
    }

    /**
     * Setter of longLasted.
     *
     * @param longLasted longLasted
     */
    public void setLongLasted(boolean longLasted) {
        this.longLasted = longLasted;
    }

    /**
     * Getter of createDate.
     *
     * @return createDate
     */
    public Date getCreateDate() {
        return createDate;
    }

    /**
     * Setter of createDate.
     *
     * @param createDate createDate
     */
    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    /**
     * Getter of lastConnectionDate.
     *
     * @return lastConnectionDate
     */
    public Date getLastConnectionDate() {
        return lastConnectionDate;
    }

    /**
     * Setter of lastConnectionDate.
     *
     * @param lastConnectionDate lastConnectionDate
     */
    public void setLastConnectionDate(Date lastConnectionDate) {
        this.lastConnectionDate = lastConnectionDate;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("id", "**hidden**")
                .add("userId", userId)
                .add("longLasted", longLasted)
                .toString();
    }
} public class User {
    /**
     * User ID.
     */
    private String id;
    
    /**
     * Locale ID.
     */
    private String roleId;
    
    /**
     * User's username.
     */
    private String username;
    
    /**
     * User's password.
     */
    private String password;

    /**
     * Email address.
     */
    private String email;
    
    /**
     * True if the user hasn't dismissed the first connection screen.
     */
    private boolean firstConnection;

    /**
     * Creation date.
     */
    private Date createDate;
    
    /**
     * Deletion date.
     */
    private Date deleteDate;

    public User() {
    }

    public User(String id, String roleId, String username, String password, String email, boolean firstConnection, Date createDate, Date deleteDate) {
        this.id = id;
        this.roleId = roleId;
        this.username = username;
        this.password = password;
        this.email = email;
        this.firstConnection = firstConnection;
        this.createDate = createDate;
        this.deleteDate = deleteDate;
    }

    /**
     * Getter of id.
     *
     * @return id
     */
    public String getId() {
        return id;
    }

    /**
     * Setter of id.
     *
     * @param id id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * Getter of roleId.
     *
     * @return roleId
     */
    public String getRoleId() {
        return roleId;
    }

    /**
     * Setter of roleId.
     *
     * @param roleId roleId
     */
    public void setRoleId(String roleId) {
        this.roleId = roleId;
    }

    /**
     * Getter of username.
     *
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * Setter of username.
     *
     * @param username username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * Getter of password.
     *
     * @return password
     */
    public String getPassword() {
        return password;
    }

    /**
     * Setter of password.
     *
     * @param password password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * Getter of email.
     *
     * @return email
     */
    public String getEmail() {
        return email;
    }

    /**
     * Setter of email.
     *
     * @param email email
     */
    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * Getter of firstConnection.
     *
     * @return firstConnection
     */
    public boolean isFirstConnection() {
        return firstConnection;
    }

    /**
     * Setter of firstConnection.
     *
     * @param firstConnection firstConnection
     */
    public void setFirstConnection(boolean firstConnection) {
        this.firstConnection = firstConnection;
    }

    /**
     * Getter of createDate.
     *
     * @return createDate
     */
    public Date getCreateDate() {
        return createDate;
    }

    /**
     * Setter of createDate.
     *
     * @param createDate createDate
     */
    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    /**
     * Getter of deleteDate.
     *
     * @return deleteDate
     */
    public Date getDeleteDate() {
        return deleteDate;
    }

    /**
     * Setter of deleteDate.
     *
     * @param deleteDate deleteDate
     */
    public void setDeleteDate(Date deleteDate) {
        this.deleteDate = deleteDate;
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("id", id)
                .add("username", username)
                .toString();
    }
} public class PaginatedList<T> {
    /**
     * Size of a page.
     */
    private int limit;

    /**
     * Offset of the page (in number of records).
     */
    private int offset;

    /**
     * Total number of records.
     */
    private int resultCount;
    
    /**
     * List of records of the current page.
     */
    private List<T> resultList;
    
    /**
     * Constructor of PaginatedList.
     * 
     * @param pageSize Page size
     * @param offset Offset
     */
    public PaginatedList(int pageSize, int offset) {
        this.limit = pageSize;
        this.offset = offset;
    }

    /**
     * Getter of resultCount.
     *
     * @return resultCount
     */
    public int getResultCount() {
        return resultCount;
    }

    /**
     * Setter of resultCount.
     *
     * @param resultCount resultCount
     */
    public void setResultCount(int resultCount) {
        this.resultCount = resultCount;
    }

    /**
     * Getter of resultList.
     *
     * @return resultList
     */
    public List<T> getResultList() {
        return resultList;
    }

    /**
     * Setter of resultList.
     *
     * @param resultList resultList
     */
    public void setResultList(List<T> resultList) {
        this.resultList = resultList;
    }

    /**
     * Getter of limit.
     *
     * @return limit
     */
    public int getLimit() {
        return limit;
    }

    /**
     * Getter of offset.
     *
     * @return offset
     */
    public int getOffset() {
        return offset;
    }
} public class PaginatedLists {
    /**
     * Default size of a page.
     */
    private static final int DEFAULT_PAGE_SIZE = 10;

    /**
     * Maximum size of a page.
     */
    private static final int MAX_PAGE_SIZE = 100;

    /**
     * Constructs a paginated list.
     * 
     * @param pageSize Size of the page
     * @param offset Offset of the page
     * @return Paginated list
     */
    public static <E> PaginatedList<E> create(Integer pageSize, Integer offset) {
        if (pageSize == null) {
            pageSize = DEFAULT_PAGE_SIZE;
        }
        if (offset == null) {
            offset = 0;
        }
        if (pageSize > MAX_PAGE_SIZE) {
            pageSize = MAX_PAGE_SIZE;
        }
        return new PaginatedList<E>(pageSize, offset);
    }
    
    /**
     * Constructs a paginated list with default parameters.
     * 
     * @return Paginated list
     */
    public static <E> PaginatedList<E> create() {
        return create(null, null);
    }
    
    /**
     * Executes a native count(*) request to count the number of results.
     * 
     * @param paginatedList Paginated list object containing parameters, and into which results are added by side effects
     * @param queryParam Query parameters
     */
    public static <E> void executeCountQuery(PaginatedList<E> paginatedList, QueryParam queryParam) {
        StringBuilder sb = new StringBuilder("select count(*) as result_count from (");
        sb.append(queryParam.getQueryString());
        sb.append(") as t1");
        
        QueryParam countQueryParam = new QueryParam(sb.toString(), queryParam.getParameterMap());
        
        Integer resultCount = QueryUtil.getNativeQuery(countQueryParam).map(IntegerMapper.FIRST).first();
        paginatedList.setResultCount(resultCount);
    }

    /**
     * Executes a query and returns the data of the currunt page.
     * 
     * @param paginatedList Paginated list object containing parameters, and into which results are added by side effects
     * @param queryParam Query parameters
     * @return List of results
     */
    private static <E> List<Object[]> executeResultQuery(PaginatedList<E> paginatedList, QueryParam queryParam) {
        Query<Object[]> q = QueryUtil.getNativeQuery(queryParam).map(ColumnIndexMapper.INSTANCE);
        
        //q.setOffset(paginatedList.getOffset()); // TODO how to do offset?
        q.setMaxRows(paginatedList.getLimit());
        return q.list();
    }
    
    /**
     * Executes a paginated request with 2 native queries (one to count the number of results, and one to return the page).
     * 
     * @param paginatedList Paginated list object containing parameters, and into which results are added by side effects
     * @param queryParam Query parameters
     * @param doCount Count the total number of rows
     * @return List of results
     */
    public static <E> List<Object[]> executePaginatedQuery(PaginatedList<E> paginatedList, QueryParam queryParam, boolean doCount) {
        if (doCount) {
            executeCountQuery(paginatedList, queryParam);
        }
        return executeResultQuery(paginatedList, queryParam);
    }

    /**
     * Executes a paginated request with 2 native queries (one to count the number of results, and one to return the page).
     * 
     * @param paginatedList Paginated list object containing parameters, and into which results are added by side effects
     * @param queryParam Query parameters
     * @param sortCriteria Sort criteria
     * @param doCount Count the total number of rows
     * @return List of results
     */
    public static <E> List<Object[]> executePaginatedQuery(PaginatedList<E> paginatedList, QueryParam queryParam, SortCriteria sortCriteria, boolean doCount) {
        StringBuilder sb = new StringBuilder(queryParam.getQueryString());
        sb.append(" order by c");
        sb.append(sortCriteria.getColumn());
        sb.append(sortCriteria.isAsc() ? " asc" : " desc");
        
        QueryParam sortedQueryParam = new QueryParam(sb.toString(), queryParam.getParameterMap());

        if (doCount) {
            executeCountQuery(paginatedList, sortedQueryParam);
        }
        return executeResultQuery(paginatedList, sortedQueryParam);
    }
} public class SortCriteria {
    /**
     * Index of the column to sort (first is 0).
     */
    private int column;
    
    /**
     * Sort in increasing order (or else decreasing).
     */
    private boolean asc = true;

    /**
     * Constructor of sortCriteria.
     */
    public SortCriteria(Integer column, Boolean asc) {
        if (column != null) {
            this.column = column;
        }
        if (asc != null) {
            this.asc = asc;
        }
    }
    
    /**
     * Getter of column.
     *
     * @return column
     */
    public int getColumn() {
        return column;
    }

    /**
     * Getter of asc.
     *
     * @return asc
     */
    public boolean isAsc() {
        return asc;
    }
} public enum BaseFunction {

    /**
     * Allows the user to use the admin fonctions.
     */
    ADMIN,

    /**
     * Allows the user to change his password. 
     */
    PASSWORD,
} public class ClientException extends WebApplicationException {
    /**
     * Serial UID.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Logger.
     */
    private static final Logger log = LoggerFactory.getLogger(ClientException.class);

    /**
     * Constructor of ClientException.
     * 
     * @param type Error type (e.g. AlreadyExistingEmail, ValidationError)
     * @param message Human readable error message
     * @param e Readable error message
     */
    public ClientException(String type, String message, Exception e) {
        this(type, message);
        log.error(type + ": " + message, e);
    }

    /**
     * Constructor of ClientException.
     * 
     * @param type Error type (e.g. AlreadyExistingEmail, ValidationError)
     * @param message Human readable error message
     */
    public ClientException(String type, String message) {
        super(Response.status(Status.BAD_REQUEST).entity(Json.createObjectBuilder()
            .add("type", type)
            .add("message", message).build()).build());
    }
} public class ValidationUtil {
    private static Pattern EMAIL_PATTERN = Pattern.compile(".+@.+\\..+");
    
    private static Pattern HTTP_URL_PATTERN = Pattern.compile("https?://.+");
    
    private static Pattern ALPHANUMERIC_PATTERN = Pattern.compile("[a-zA-Z0-9_]+");
    
    /**
     * Checks that the argument is not null.
     * 
     * @param s Object tu validate
     * @param name Name of the parameter
     * @throws ClientException
     */
    public static void validateRequired(Object s, String name) throws ClientException {
        if (s == null) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be set", name));
        }
    }
    
    /**
     * Validate a string length.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @param lengthMin Minimum length (or null)
     * @param lengthMax Maximum length (or null)
     * @param nullable True if the string can be empty or null
     * @return String without white spaces
     * @throws ClientException
     */
    public static String validateLength(String s, String name, Integer lengthMin, Integer lengthMax, boolean nullable) throws ClientException {
        s = StringUtils.strip(s);
        if (nullable && StringUtils.isEmpty(s)) {
            return s;
        }
        if (s == null) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be set", name));
        }
        if (lengthMin != null && s.length() < lengthMin) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be more than {1} characters", name, lengthMin));
        }
        if (lengthMax != null && s.length() > lengthMax) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be more than {1} characters", name, lengthMax));
        }
        return s;
    }
    
    /**
     * Validate a string length. The string mustn't be empty.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @param lengthMin Minimum length (or null)
     * @param lengthMax Maximum length (or null)
     * @return String without white spaces
     * @throws ClientException
     */
    public static String validateLength(String s, String name, Integer lengthMin, Integer lengthMax) throws ClientException {
        return validateLength(s, name, lengthMin, lengthMax, false);
    }
    
    /**
     * Checks if the string is not null and is not only whitespaces.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @return String without white spaces
     * @throws ClientException
     */
    public static String validateStringNotBlank(String s, String name) throws ClientException {
        return validateLength(s, name, 1, null, false);
    }
    
    /**
     * Checks if the string is an email.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @throws ClientException
     */
    public static void validateEmail(String s, String name) throws ClientException {
        if (!EMAIL_PATTERN.matcher(s).matches()) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be an email", name));
        }
    }
    
    /**
     * Validates that the provided string matches an URL with HTTP or HTTPS scheme.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @return Stripped URL
     * @throws ClientException
     */
    public static String validateHttpUrl(String s, String name) throws ClientException {
        s = StringUtils.strip(s);
        if (!HTTP_URL_PATTERN.matcher(s).matches()) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be an HTTP(s) URL", name));
        }
        return s;
    }
    
    /**
     * Checks if the string uses only alphanumerical or underscore characters.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @throws ClientException
     */
    public static void validateAlphanumeric(String s, String name) throws ClientException {
        if (!ALPHANUMERIC_PATTERN.matcher(s).matches()) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must have only alphanumeric or underscore characters", name));
        }
    }
    
    /**
     * Checks if the string is a number.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @return Parsed number
     * @throws ClientException
     */
    public static Integer validateInteger(String s, String name) throws ClientException {
        try {
            return Integer.valueOf(s);
        } catch (NumberFormatException e) {
            throw new ClientException("Validation Error", MessageFormat.format("{0} is not a number", name));
        }
    }
    
    /**
     * Validates and parses a date.
     * 
     * @param s String to validate
     * @param name Name of the parameter
     * @param nullable True if the string can be empty or null
     * @return Parsed date
     * @throws ClientException
     */
    public static Date validateDate(String s, String name, boolean nullable) throws ClientException {
        if (Strings.isNullOrEmpty(s)) {
            if (!nullable) {
                throw new ClientException("ValidationError", MessageFormat.format("{0} must be set", name));
            } else {
                return null;
            }
        }
        try {
            return new DateTime(Long.parseLong(s)).toDate();
        } catch (NumberFormatException e) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be a date", name));
        }
    }
    
    /**
     * Validates a directory.
     * 
     * @param s Path to the directory
     * @param name Name of the parameter
     * @param nullable True if the path can be empty or null
     * @return Directory
     * @throws ClientException
     */
    public static File validateDirectory(String s, String name, boolean nullable) throws ClientException {
        if (Strings.isNullOrEmpty(s)) {
            if (!nullable) {
                throw new ClientException("ValidationError", MessageFormat.format("{0} must be set", name));
            } else {
                return null;
            }
        }
        
        File directory = new File(s);
        if (!directory.exists() || directory.isFile()) {
            throw new ClientException("ValidationError", MessageFormat.format("{0} must be a valid directory", name));
        }
        
        return directory;
    }
} public class UserPrincipal implements IPrincipal {
    /**
     * ID of the user.
     */
    private String id;
    
    /**
     * Username of the user.
     */
    private String name;
    
    /**
     * Timezone of the principal.
     */
    private DateTimeZone dateTimeZone;
    
    /**
     * Email of the principal.
     */
    private String email;
    
    /**
     * User base functions.
     */
    private Set<String> baseFunctionSet;
    
    /**
     * Constructor of UserPrincipal.
     * 
     * @param id ID of the user
     * @param name Usrename of the user
     */
    public UserPrincipal(String id, String name) {
        this.id = id;
        this.name = name;
    }
    
    @Override
    public boolean isAnonymous() {
        return false;
    }

    @Override
    public String getId() {
        return id;
    }

    /**
     * Setter of id.
     *
     * @param id id
     */
    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String getName() {
        return name;
    }

    /**
     * Setter of name.
     *
     * @param name name
     */
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public DateTimeZone getDateTimeZone() {
        return dateTimeZone;
    }

    /**
     * Setter of dateTimeZone.
     *
     * @param dateTimeZone dateTimeZone
     */
    public void setDateTimeZone(DateTimeZone dateTimeZone) {
        this.dateTimeZone = dateTimeZone;
    }

    @Override
    public String getEmail() {
        return email;
    }
    
    /**
     * Setter of email.
     *
     * @param email email
     */
    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * Getter of baseFunctionSet.
     *
     * @return baseFunctionSet
     */
    public Set<String> getBaseFunctionSet() {
        return baseFunctionSet;
    }

    /**
     * Setter of baseFunctionSet.
     *
     * @param baseFunctionSet baseFunctionSet
     */
    public void setBaseFunctionSet(Set<String> baseFunctionSet) {
        this.baseFunctionSet = baseFunctionSet;
    }

} public class TokenBasedSecurityFilter implements Filter {
    /**
     * Logger.
     */
    private static final Logger log = LoggerFactory.getLogger(TokenBasedSecurityFilter.class);

    /**
     * Name of the cookie used to store the authentication token.
     */
    public static final String COOKIE_NAME = "auth_token";

    /**
     * Name of the attribute containing the principal.
     */
    public static final String PRINCIPAL_ATTRIBUTE = "principal";
    
    /**
     * Lifetime of the authentication token in seconds, since login.
     */
    public static final int TOKEN_LONG_LIFETIME = 3600 * 24 * 365 * 20;
    
    /**
     * Lifetime of the authentication token in seconds, since last connection.
     */
    public static final int TOKEN_SESSION_LIFETIME = 3600 * 24;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // NOP
    }

    @Override
    public void destroy() {
        // NOP
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        // Get the value of the client authentication token
        HttpServletRequest request = (HttpServletRequest) req;
        String authToken = null;
        if (request.getCookies() != null) {
            for (Cookie cookie : request.getCookies()) {
                if (COOKIE_NAME.equals(cookie.getName())) {
                    authToken = cookie.getValue();
                }
            }
        }
        
        // Get the corresponding server token
        AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
        AuthenticationToken authenticationToken = null;
        if (authToken != null) {
            authenticationToken = authenticationTokenDao.get(authToken);
        }
        
        if (authenticationToken == null) {
            injectAnonymousUser(request);
        } else {
            // Check if the token is still valid
            if (isTokenExpired(authenticationToken)) {
                try {
                    injectAnonymousUser(request);

                    // Destroy the expired token
                    authenticationTokenDao.delete(authToken);
                } catch (Exception e) {
                    if (log.isErrorEnabled()) {
                        log.error(MessageFormat.format("Error deleting authentication token {0} ", authToken), e);
                    }
                }
            } else {
                // Check if the user is still valid
                UserDao userDao = new UserDao();
                User user = userDao.getActiveById(authenticationToken.getUserId());
                if (user != null) {
                    injectAuthenticatedUser(request, user);
                    
                    // Update the last connection date
                    authenticationTokenDao.updateLastConnectionDate(authenticationToken.getId());
                } else {
                    injectAnonymousUser(request);
                }
            }
        }
        
        filterChain.doFilter(request, response);
    }
    
    /**
     * Returns true if the token is expired.
     * 
     * @param authenticationToken Authentication token
     * @return Token expired
     */
    private boolean isTokenExpired(AuthenticationToken authenticationToken) {
        final long now = new Date().getTime();
        final long creationDate = authenticationToken.getCreateDate().getTime();
        if (authenticationToken.isLongLasted()) {
            return now >= creationDate + ((long) TOKEN_LONG_LIFETIME) * 1000L;
        } else {
            long date = authenticationToken.getLastConnectionDate() != null ?
                    authenticationToken.getLastConnectionDate().getTime() : creationDate;
            return now >= date + ((long) TOKEN_SESSION_LIFETIME) * 1000L;
        }
    }

    /**
     * Inject an authenticated user into the request attributes.
     * 
     * @param request HTTP request
     * @param user User to inject
     */
    private void injectAuthenticatedUser(HttpServletRequest request, User user) {
        UserPrincipal userPrincipal = new UserPrincipal(user.getId(), user.getUsername());

        // Add base functions
        RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao();
        Set<String> baseFunctionSet = userBaseFuction.findByRoleId(user.getRoleId());
        userPrincipal.setBaseFunctionSet(baseFunctionSet);
        
        request.setAttribute(PRINCIPAL_ATTRIBUTE, userPrincipal);
    }

    /**
     * Inject an anonymous user into the request attributes.
     * 
     * @param request HTTP request
     */
    private void injectAnonymousUser(HttpServletRequest request) {
        AnonymousPrincipal anonymousPrincipal = new AnonymousPrincipal();
        anonymousPrincipal.setDateTimeZone(DateTimeZone.forID(Constants.DEFAULT_TIMEZONE_ID));

        request.setAttribute(PRINCIPAL_ATTRIBUTE, anonymousPrincipal);
    }
}
import java.util.Date;
import java.util.Set;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.servlet.http.Cookie;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;
import com.sismics.home.core.constant.Constants;
import com.sismics.home.core.dao.dbi.AuthenticationTokenDao;
import com.sismics.home.core.dao.dbi.RoleBaseFunctionDao;
import com.sismics.home.core.dao.dbi.UserDao;
import com.sismics.home.core.dao.dbi.criteria.UserCriteria;
import com.sismics.home.core.dao.dbi.dto.UserDto;
import com.sismics.home.core.event.PasswordChangedEvent;
import com.sismics.home.core.event.UserCreatedEvent;
import com.sismics.home.core.model.context.AppContext;
import com.sismics.home.core.model.dbi.AuthenticationToken;
import com.sismics.home.core.model.dbi.User;
import com.sismics.home.core.util.dbi.PaginatedList;
import com.sismics.home.core.util.dbi.PaginatedLists;
import com.sismics.home.core.util.dbi.SortCriteria;
import com.sismics.home.rest.constant.BaseFunction;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.ValidationUtil;
import com.sismics.security.UserPrincipal;
import com.sismics.util.filter.TokenBasedSecurityFilter;
package com.sismics.home.rest.resource;





/**
 * User REST resources.
 * 
 * @author jtremeaux
 */
@Path("/user")
public class UserResource extends BaseResource {
    /**
     * Creates a new user.
     * 
     * @param username User's username
     * @param password Password
     * @param email E-Mail
     * @param localeId Locale ID
     * @return Response
     */
    @PUT
    @Produces(MediaType.APPLICATION_JSON)
    public Response register(
        @FormParam("username") String username,
        @FormParam("password") String password,
        @FormParam("locale") String localeId,
        @FormParam("email") String email) {

        if (!authenticate()) {
            throw new ForbiddenClientException();
        }
        checkBaseFunction(BaseFunction.ADMIN);
        
        // Validate the input data
        username = ValidationUtil.validateLength(username, "username", 3, 50);