/*
* Copyright (C) 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package apps.provisioning.server.account.data;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.PriorityQueue;
import apps.provisioning.config.ConfigData;
/**
* In-memory data source that locks usernames to prevent to be taken meanwhile user is choosing one
* option.
*/
public class LockedDirectory implements UsernameDataSource {
/**
* Class to compare entries by their timestamp.
*
*/
private class LockedDirectoryComparator implements Comparator<LockedDirectoryEntry> {
/*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(LockedDirectoryEntry entry1, LockedDirectoryEntry entry2) {
if (entry1.equals(entry2)) {
return 0;
}
if (entry1.getTimestamp().after(entry2.getTimestamp())) {
return 1;
} else {
return -1;
}
}
}
/**
* Contains a locked username and the time when it was locked. Used to keep track of expired
* usernames.
*/
private class LockedDirectoryEntry {
String username;
Timestamp timestamp;
public LockedDirectoryEntry(String username) {
this.username = username;
timestamp = new Timestamp(System.currentTimeMillis());
}
public Timestamp getTimestamp() {
return timestamp;
}
public String getUsername() {
return username;
}
@Override
public boolean equals(Object entry) {
return ((LockedDirectoryEntry) entry).getUsername().equals(username);
}
}
// In-memory data source.
private PriorityQueue<LockedDirectoryEntry> lockedEntries;
private long suggestedUsernamesTimeout;
public LockedDirectory(ConfigData config) {
lockedEntries = new PriorityQueue<LockedDirectory.LockedDirectoryEntry>(
10, new LockedDirectoryComparator());
// Get the suggested usernames timeout and convert it to milliseconds.
suggestedUsernamesTimeout = config.getSuggestedUsernamesTimeout() * 1000;
}
public synchronized boolean exists(String username) {
removeExpiredUsernames();
return lockedEntries.contains(new LockedDirectoryEntry(username));
}
public synchronized void insert(String username) throws Exception {
if (exists(username)) {
throw new Exception("Username alrealy exists in Locked Directory.");
}
lockedEntries.add(new LockedDirectoryEntry(username));
}
/**
* Removes expired usernames.
*/
private void removeExpiredUsernames() {
if (lockedEntries.size() == 0) {
return;
}
Timestamp now = new Timestamp(System.currentTimeMillis());
LockedDirectoryEntry entry = lockedEntries.peek();
while (entry != null) {
// The peek of the queue is always the oldest entry. Check if it has expired and if so, remove
// it.
Timestamp oldestTimestamp = entry.getTimestamp();
long diff = now.getTime() - oldestTimestamp.getTime();
if (diff > suggestedUsernamesTimeout) {
// Remove the expired entry.
lockedEntries.poll();
// Get the new oldest entry so it is checked it in the next loop.
entry = lockedEntries.peek();
} else {
// The oldest entry is not expired yet. It is safe to assume the rest of the entries are
// still valid.
entry = null;
}
}
}
/**
* Removes a username from the locked data source.
*
* @param username The user name.
* @return Whether the user was deleted.
*/
public boolean remove(String username) {
return lockedEntries.remove(new LockedDirectoryEntry(username));
}
/**
* Removes usernames from the locked data source.
*
* @param usernames Usernames collection.
*/
public void removeMultiple(Collection<String> usernames) {
for (String username : usernames) {
lockedEntries.remove(new LockedDirectoryEntry(username));
}
}
/**
* Inserts multiple usernames to the locked data source.
*
* @param usernames ArrayList with usernames to be added.
*/
public void insertMultiple(ArrayList<String> usernames) {
for (String username : usernames) {
lockedEntries.add(new LockedDirectoryEntry(username));
}
}
/**
* Clears the HashMap.
*/
public void clear() {
lockedEntries.clear();
}
}