/*
* Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com).
*
* 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 och.front.service.model;
import static java.util.Collections.*;
import static och.api.model.chat.account.PrivilegeType.*;
import static och.util.Util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import och.api.exception.chat.ChatOwnerNotFoundException;
import och.api.exception.chat.NoChatAccountException;
import och.api.exception.tariff.TariffNotFoundException;
import och.api.model.chat.account.ChatAccount;
import och.api.model.chat.account.ChatAccountPrivileges;
import och.api.model.chat.account.PrivilegeType;
import och.api.model.chat.config.Key;
import och.api.model.server.ServerRow;
import och.api.model.tariff.Tariff;
import org.apache.commons.logging.Log;
public class ChatsModel {
private Log log = getLog(getClass());
private HashMap<Long, ServerRow> serversById = new HashMap<>();
private HashMap<Long, ChatAccount> accountsById = new HashMap<>();
private HashMap<String, ChatAccount> accountsByUid = new HashMap<>();
private HashMap<Long, List<ChatAccountPrivileges>> privilegesByUser = new HashMap<>();
private HashMap<Long, Tariff> tariffsById = new HashMap<Long, Tariff>();
private ReadWriteLock rw = new ReentrantReadWriteLock();
private Lock read = rw.readLock();
private Lock write = rw.writeLock();
public void init(
List<ServerRow> servers,
List<ChatAccount> accounts,
List<ChatAccountPrivileges> privileges,
List<Tariff> tariffs){
write.lock();
try {
for(ServerRow server : servers){
serversById.put(server.id, server);
}
for(ChatAccount account : accounts){
accountsById.put(account.id, account);
accountsByUid.put(account.uid, account);
}
for(ChatAccountPrivileges priv : privileges){
putToListMap(privilegesByUser, priv.userId, priv);
}
for(Tariff t : tariffs){
tariffsById.put(t.id, t);
}
} finally {
write.unlock();
}
}
public void putData(ServerRow server, ChatAccount account, ChatAccountPrivileges ownerPriv){
write.lock();
try {
serversById.put(server.id, server);
accountsById.put(account.id, account);
accountsByUid.put(account.uid, account);
putToListMap(privilegesByUser, ownerPriv.userId, ownerPriv);
}finally {
write.unlock();
}
}
public void putServer(ServerRow server){
write.lock();
try {
ServerRow clone = server.clone();
serversById.put(server.id, clone);
} finally {
write.unlock();
}
}
public void removeServer(long serverId){
write.lock();
try {
serversById.remove(serverId);
} finally {
write.unlock();
}
}
public void putAcc(ChatAccount acc){
write.lock();
try {
ChatAccount clone = acc.clone();
accountsById.put(clone.id, clone);
accountsByUid.put(clone.uid, clone);
} finally {
write.unlock();
}
}
public void removeAcc(String accUid){
write.lock();
try {
ChatAccount removed = accountsByUid.remove(accUid);
if(removed != null){
accountsById.remove(removed.id);
}
} finally {
write.unlock();
}
}
public void putTariff(Tariff tariff){
write.lock();
try {
Tariff clone = tariff.clone();
tariffsById.put(clone.id, clone);
} finally {
write.unlock();
}
}
public void updateServerFull(long serverId, boolean val){
write.lock();
try {
ServerRow server = serversById.get(serverId);
if(server == null) return;
server.setFull(val);
} finally {
write.unlock();
}
}
public void putAccConfig(String accUid, Key key, Object val){
write.lock();
try {
ChatAccount acc = accountsByUid.get(accUid);
if(acc == null) return;
if(key == Key.name){
acc.name = val == null? null : val.toString();
}
else if(key == Key.feedback_notifyOpsByEmail){
acc.setFeedback_notifyOpsByEmail(tryParseBool(val, null));
}
}finally {
write.unlock();
}
}
public void addPrivileges(long userId, long accId, Set<PrivilegeType> set){
write.lock();
try {
ChatAccountPrivileges priv = getPrivsForUser(userId, accId, true);
priv.privileges.addAll(set);
}finally {
write.unlock();
}
}
public void putPrivs(ChatAccountPrivileges privs){
write.lock();
try {
List<ChatAccountPrivileges> list = privilegesByUser.get(privs.userId);
if(list == null){
list = new ArrayList<>();
privilegesByUser.put(privs.userId, list);
}
int oldIndex = getPrivsIndex(list, privs.userId, privs.accId);
if(oldIndex > -1) list.remove(oldIndex);
list.add(privs);
}finally {
write.unlock();
}
}
public void removePrivileges(long userId, long accId, Set<PrivilegeType> set){
write.lock();
try {
ChatAccountPrivileges priv = getPrivsForUser(userId, accId, false);
if(priv == null) return;
priv.privileges.removeAll(set);
if(priv.privileges.isEmpty()) {
removePrivilegeUnsafe(userId, accId);
}
}finally {
write.unlock();
}
}
public void removePrivileges(long userId, long accId){
write.lock();
try {
removePrivilegeUnsafe(userId, accId);
}finally {
write.unlock();
}
}
private void removePrivilegeUnsafe(long userId, long accId) {
List<ChatAccountPrivileges> list = privilegesByUser.get(userId);
int index = getPrivsIndex(list, userId, accId);
if(index == -1) return;
list.remove(index);
if(list.isEmpty()) privilegesByUser.remove(userId);
}
private int getPrivsIndex(List<ChatAccountPrivileges> list, long userId, long accId){
if(isEmpty(list)) return -1;
for (int i = 0; i < list.size(); i++) {
if(list.get(i).accId == accId)
return i;
}
return -1;
}
public void updateAccTariff(String accUid, long tariffId, Date tariffStart, int changedInDay, Long tariffPrevId){
write.lock();
try {
ChatAccount acc = accountsByUid.get(accUid);
if(acc == null) return;
acc.tariffId = tariffId;
acc.tariffStart = tariffStart;
acc.tariffLastPay = tariffStart;
acc.tariffChangedInDay = changedInDay;
acc.tariffPrevId = tariffPrevId;
}finally {
write.unlock();
}
}
public boolean setNickname(String accUid, long userId, String nickname) {
write.lock();
try {
ChatAccount acc = accountsByUid.get(accUid);
if(acc == null) return false;
List<ChatAccountPrivileges> privs = privilegesByUser.get(userId);
if(isEmpty(privs)) return false;
ChatAccountPrivileges priv = null;
for (ChatAccountPrivileges curPriv : privs) {
if(curPriv.accId == acc.id){
priv = curPriv;
break;
}
}
if(priv == null) return false;
priv.nickname = nickname;
return true;
}finally {
write.unlock();
}
}
public ServerRow getServerByAcc(String accUid) {
read.lock();
try {
ChatAccount chatAccount = accountsByUid.get(accUid);
if(chatAccount == null) return null;
ServerRow server = serversById.get(chatAccount.serverId);
return server == null? null : server.clone();
} finally {
read.unlock();
}
}
public UserAccInfo getPrivilegesForAcc(long userId, long accId) {
read.lock();
try {
List<ChatAccountPrivileges> allUserPrivs = privilegesByUser.get(userId);
if(allUserPrivs == null) return new UserAccInfo();
for (ChatAccountPrivileges privs : allUserPrivs) {
if(privs.accId == accId){
return new UserAccInfo(privs);
}
}
return new UserAccInfo();
} finally {
read.unlock();
}
}
public UserAccInfo getPrivilegesForAcc(long userId, String accUid) {
read.lock();
try {
List<ChatAccountPrivileges> allUserPrivs = privilegesByUser.get(userId);
if(allUserPrivs == null) return new UserAccInfo();
for (ChatAccountPrivileges privs : allUserPrivs) {
ChatAccount acc = accountsById.get(privs.accId);
if(acc == null) continue;
if(acc.uid.equals(accUid)){
return new UserAccInfo(privs);
}
}
return new UserAccInfo();
} finally {
read.unlock();
}
}
public Tariff getTariff(long tariffId){
read.lock();
try {
Tariff t = tariffsById.get(tariffId);
Tariff clone = t == null? null : t.clone();
return clone;
}finally {
read.unlock();
}
}
public Tariff findTariff(long tariffId) throws TariffNotFoundException {
Tariff t = getTariff(tariffId);
if(t == null) throw new TariffNotFoundException(tariffId);
return t;
}
public List<Tariff> getPublicTariffs() {
read.lock();
try {
ArrayList<Tariff> out = new ArrayList<Tariff>();
for (Tariff t : tariffsById.values()) {
if(t.isPublic) {
Tariff clone = t.clone();
out.add(clone);
}
}
return out;
}finally {
read.unlock();
}
}
public Map<String, Set<PrivilegeType>> getPrivilegesForAccs(long userId) {
read.lock();
try {
List<ChatAccountPrivileges> allUserPrivs = privilegesByUser.get(userId);
if(allUserPrivs == null) return emptyMap();
Map<String, Set<PrivilegeType>> out = new HashMap<>();
for (ChatAccountPrivileges privs : allUserPrivs) {
ChatAccount acc = accountsById.get(privs.accId);
if(acc == null) continue;
out.put(acc.uid, privs.clonePrivileges());
}
return out;
} finally {
read.unlock();
}
}
/**
* Список аккаунтов с серверами в которых юзер - оператор или модерадор или админ.
* Root роль не дает права получить все аккаунты.
*/
public List<ChatAccount> getAccountsForOperator(long userId) {
read.lock();
try {
List<ChatAccountPrivileges> privs = privilegesByUser.get(userId);
if(isEmpty(privs)) return emptyList();
HashSet<ChatAccount> set = new HashSet<>();
for (ChatAccountPrivileges priv : privs) {
if( priv.privileges.isEmpty()) continue;
ChatAccount acc = accountsById.get(priv.accId);
if(acc == null) continue;
if(set.contains(acc)) continue;
ServerRow server = serversById.get(acc.serverId);
if(server == null) continue;
ChatAccount clone = acc.clone();
clone.server = server.clone();
set.add(clone);
}
ArrayList<ChatAccount> out = new ArrayList<>(set);
sort(out);
return out;
}finally {
read.unlock();
}
}
/**
* Список uid аккаунтов в которых юзер - оператор или модерадор или админ.
* Root роль не дает права получить все аккаунты.
*/
public Set<String> getAccountsUidsForAnyPriv(long userId) {
return getAccountsUidsFor(userId);
}
/**
* Список uid аккаунтов в которых юзер - админ.
* Root роль не дает права получить все аккаунты.
*/
public Set<String> getAccountsUidsFor(long userId, PrivilegeType... targetPrivs) {
read.lock();
try {
List<ChatAccountPrivileges> privs = privilegesByUser.get(userId);
if(isEmpty(privs)) return set();
HashSet<String> set = new HashSet<>();
for (ChatAccountPrivileges priv : privs) {
Set<PrivilegeType> curAccPrivs = priv.privileges;
if( curAccPrivs.isEmpty()) continue;
boolean validAcc = targetPrivs.length == 0? true : false;
for(PrivilegeType targetPriv : targetPrivs){
if(curAccPrivs.contains(targetPriv)){
validAcc = true;
break;
}
}
if( ! validAcc) continue;
ChatAccount acc = accountsById.get(priv.accId);
if(acc == null) continue;
if(set.contains(acc)) continue;
ServerRow server = serversById.get(acc.serverId);
if(server == null) continue;
set.add(acc.uid);
}
return set;
}finally {
read.unlock();
}
}
public ServerRow getServer(long serverId){
read.lock();
try {
ServerRow server = serversById.get(serverId);
return server == null? null : server.clone();
} finally {
read.unlock();
}
}
public List<ServerRow> getServers(){
read.lock();
try {
ArrayList<ServerRow> out = new ArrayList<>();
for (ServerRow s : serversById.values()) {
ServerRow clone = s.clone();
out.add(clone);
}
return out;
}finally {
read.unlock();
}
}
public List<Long> getNotFullServersId(){
read.lock();
try {
ArrayList<Long> out = new ArrayList<Long>();
for (ServerRow server : serversById.values()) {
if( ! server.isFull) out.add(server.id);
}
return out;
}finally {
read.unlock();
}
}
public int getServerAccountsCount(long serverId){
read.lock();
try {
int count = 0;
for (ChatAccount acc : accountsById.values()) {
if(acc.serverId == serverId){
count++;
}
}
return count;
}finally {
read.unlock();
}
}
public List<ChatAccount> getServerAccounts(long serverId){
read.lock();
try {
ArrayList<ChatAccount> out = new ArrayList<>();
for (ChatAccount acc : accountsById.values()) {
if(acc.serverId == serverId){
ChatAccount clone = acc.clone();
out.add(clone);
}
}
return out;
}finally {
read.unlock();
}
}
public ChatAccount findAccount(String accUid, boolean withServer) throws NoChatAccountException {
ChatAccount acc = getAccount(accUid, withServer);
if(acc == null) throw new NoChatAccountException();
return acc;
}
public ChatAccount getAccount(String accUid, boolean withServer){
read.lock();
try {
ChatAccount acc = accountsByUid.get(accUid);
if(acc == null) return null;
ServerRow server = serversById.get(acc.serverId);
if(server == null) return null;
ChatAccount clone = acc.clone();
if(withServer) clone.server = server.clone();
return clone;
}finally {
read.unlock();
}
}
public ChatAccount getAccount(long accId, boolean withServer){
read.lock();
try {
ChatAccount acc = accountsById.get(accId);
if(acc == null) return null;
ServerRow server = serversById.get(acc.serverId);
if(server == null) return null;
ChatAccount clone = acc.clone();
if(withServer) clone.server = server.clone();
return clone;
}finally {
read.unlock();
}
}
public Map<Long, UserAccInfo> getAccUsers(String accUid){
return getAccUsersWithPrivs(accUid);
}
public Map<Long, UserAccInfo> getAccOperators(String accUid){
return getAccUsersWithPrivs(accUid, CHAT_OPERATOR);
}
public Long getAccOwner(String accUid){
Map<Long, UserAccInfo> users = getAccUsersWithPrivs(accUid, CHAT_OWNER);
if(isEmpty(users)) return null;
if(users.size() > 1) log.warn("account "+accUid+" has many owners: "+users.keySet());
return firstFrom(users.keySet());
}
public long findAccOwner(String accUid) throws ChatOwnerNotFoundException {
Long ownerId = getAccOwner(accUid);
if(ownerId == null) throw new ChatOwnerNotFoundException();
return ownerId;
}
public Map<Long, UserAccInfo> getAccUsersWithPrivs(String accUid, PrivilegeType... validPrivs){
read.lock();
try {
ChatAccount acc = accountsByUid.get(accUid);
if(acc == null) return emptyMap();
HashMap<Long, UserAccInfo> out = new HashMap<>();
nextUser: for (Entry<Long, List<ChatAccountPrivileges>> entry : privilegesByUser.entrySet()) {
List<ChatAccountPrivileges> privs = entry.getValue();
if(isEmpty(privs)) continue;
for (ChatAccountPrivileges priv : privs) {
//user of acc
if(priv.accId == acc.id){
//filter by priv
if( ! isEmpty(validPrivs)){
boolean validUser = false;
for(PrivilegeType validPriv : validPrivs){
if(priv.privileges.contains(validPriv)){
validUser = true;
break;
}
}
if(!validUser) continue nextUser;
}
Long userId = entry.getKey();
UserAccInfo userAccInfo = new UserAccInfo(priv);
out.put(userId, userAccInfo);
continue nextUser;
}
}
}
return out;
}finally {
read.unlock();
}
}
public Map<String, Long> getAllAccOwners(){
read.lock();
try {
HashMap<String, Long> accOwners = new HashMap<String, Long>();
nextUser: for (Entry<Long, List<ChatAccountPrivileges>> entry : privilegesByUser.entrySet()) {
List<ChatAccountPrivileges> privs = entry.getValue();
if(isEmpty(privs)) continue;
for (ChatAccountPrivileges priv : privs) {
if(priv.privileges.contains(CHAT_OWNER)){
Long accId = priv.accId;
ChatAccount acc = accountsById.get(accId);
if(acc != null){
accOwners.put(acc.uid, priv.userId);
continue nextUser;
}
}
}
}
return accOwners;
}finally {
read.unlock();
}
}
private ChatAccountPrivileges getPrivsForUser(long userId, long accId, boolean createIfNull) {
List<ChatAccountPrivileges> list = privilegesByUser.get(userId);
if(isEmpty(list)){
if( ! createIfNull) return null;
list = new ArrayList<>();
privilegesByUser.put(userId, list);
}
ChatAccountPrivileges found = null;
for(ChatAccountPrivileges candidat : list){
if(candidat.accId == accId){
found = candidat;
break;
}
}
if(found == null && createIfNull){
found = new ChatAccountPrivileges(userId, accId);
list.add(found);
}
return found;
}
}