/*
* 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.chat.web.servlet.test;
import static och.api.model.user.SecurityContext.*;
import static och.util.Util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import och.api.model.chat.ChatLog;
import och.api.model.chat.ChatOperator;
import och.api.model.client.ClientSession;
import och.chat.web.JsonGetServlet;
import och.chat.web.model.ChatLogResp;
import och.chat.web.servlet.test.StressTestServlet;
import och.util.NumberUtil;
@SuppressWarnings("serial")
@WebServlet("/test/stressTest")
public class StressTestServlet extends JsonGetServlet<Object> {
static final int accCount = 100;
static final int sessionsCountForAcc = 10;
static final int maxCommentsForSession = 6;
Random r = new Random();
ArrayList<String> accs = new ArrayList<>();
ChatOperator op1 = new ChatOperator(1, "oooop1");
ChatOperator op2 = new ChatOperator(2, "oooop2");
ConcurrentHashMap<String, UserSessions> userSessionsByAcc = new ConcurrentHashMap<>();
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
for (int i = 0; i < accCount; i++) {
accs.add("stress-test-"+NumberUtil.zeroFormattedStr(i+1, 4));
}
//create test accs
pushToSecurityContext_SYSTEM_USER();
try {
Collection<String> curChats = chats.getAccIds();
if( ! curChats.contains(accs.get(accs.size()-1))){
log.info("create test accs...");
for (String accId : accs) {
chats.createAcc(accId);
chats.putOperator(accId, op1);
chats.putOperator(accId, op2);
}
log.info("done");
}
log.info("init chats active ops...");
for (String accId : accs) {
chats.setActiveOperator(accId, op1.id);
chats.setActiveOperator(accId, op2.id);
}
log.info("done");
}finally {
popUserFromSecurityContext();
}
}
@Override
protected Object doJsonGet(HttpServletRequest req, HttpServletResponse resp) throws Throwable {
boolean readOnly = req.getParameter("read-only") != null;
boolean writeOnly = req.getParameter("write-only") != null;
//70% read op
//30% write op
if( ! writeOnly && (readOnly || r.nextInt(100)+1 <= 70)){
return readOp(req);
} else {
return wrireOp(req);
}
}
private Object wrireOp(HttpServletRequest req) throws Exception {
String accId = accs.get(r.nextInt(accCount));
boolean isUser = r.nextInt(100) < 70;
if(isUser){
return addUserComment(req, accId);
} else {
return addOpComment(req, accId);
}
}
private Object addUserComment(HttpServletRequest req, String accId) {
UserSessions newSessions = new UserSessions(this);
UserSessions sessions = userSessionsByAcc.putIfAbsent(accId, newSessions);
if(sessions == null){
sessions = newSessions;
}
ClientSession clientSession = sessions.getSession();
clientSession.addAccId(accId);
chats.addComment(accId, clientSession, "текст от клиента!! длыоадфы фдвыдаофы фывдавоа 1223 dasadfa");
ChatLog chatLog = chats.getChatLog(accId, clientSession);
return chatLog == null? null : new ChatLogResp(chatLog);
}
void closeSession(ClientSession session) {
chats.closeChats(session);
}
private Object addOpComment(HttpServletRequest req, String accId) {
ChatOperator op = r.nextBoolean()? op1 : op2;
long operatorId = op.id;
Collection<ChatLog> state = app.chats.getActiveChatLogs(accId, operatorId);
if(isEmpty(state)) return "can't addOpComment: no chats for accId="+accId+", opId="+operatorId;
ArrayList<ChatLog> logs = new ArrayList<>(state);
ChatLog randomChat = logs.get(r.nextInt(logs.size()));
chats.addComment(accId, randomChat.id, operatorId, "Текст от оператора -- "+new Date());
ChatLog chatLog = chats.getChatLogById(accId, randomChat.id);
return chatLog == null? null : new ChatLogResp(chatLog);
}
private Object readOp(HttpServletRequest req) throws Exception {
String accId = accs.get(r.nextInt(accCount));
ChatOperator op = r.nextBoolean()? op1 : op2;
long operatorId = op.id;
Collection<ChatLog> state = app.chats.getActiveChatLogs(accId, operatorId);
if(isEmpty(state)) return "can't readOp: no chats for accId="+accId+", opId="+operatorId;
ArrayList<ChatLog> logs = new ArrayList<>(state);
ChatLog randomChat = logs.get(r.nextInt(logs.size()));
ChatLog chatLog = chats.getChatLogById(accId, randomChat.id);
return chatLog == null? null : new ChatLogResp(chatLog);
}
//clients
static class UserSessions {
private StressTestServlet owner;
private ConcurrentHashMap<String, SessionState> statesById = new ConcurrentHashMap<>();
private Random r = new Random();
public UserSessions(StressTestServlet owner) {
this.owner = owner;
}
public UserSessions(){
for (int i = 0; i < sessionsCountForAcc; i++) {
ClientSession session = ClientSession.randomSession();
statesById.put(session.sessionId, new SessionState(session, 0));
}
}
public ClientSession getSession() {
SessionState curState = null;
ArrayList<String> keys = new ArrayList<>(statesById.keySet());
if(keys.size() > 0){
String randomId = keys.get(r.nextInt(keys.size()));
curState = statesById.remove(randomId);
}
if(curState == null){
curState = new SessionState(ClientSession.randomSession(), 0);
}
ClientSession session = curState.session;
int newReqCount = curState.reqCount + 1;
if(newReqCount > maxCommentsForSession){
owner.closeSession(session);
session = ClientSession.randomSession();
newReqCount = 0;
}
SessionState newState = new SessionState(session, newReqCount);
statesById.put(session.sessionId, newState);
return session;
}
}
static class SessionState {
public final ClientSession session;
public final int reqCount;
public SessionState(ClientSession session, int reqCount) {
this.session = session;
this.reqCount = reqCount;
}
}
}