/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* BPS Bildungsportal Sachsen GmbH, http://www.bps-system.de
* <p>
*/
package de.bps.onyx.util;
import java.util.LinkedList;
import java.util.List;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.commons.services.taskexecutor.TaskExecutorManager;
import org.olat.core.id.Identity;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
import org.olat.course.nodes.CourseNode;
import org.olat.ims.qti.QTIResultSet;
import de.bps.onyx.plugin.wsserver.TestState;
public class ExamPoolManagerProvider implements MessageListener {
protected static final String JMS_RESPONSE_STATUS_PROPERTY_NAME = "response_status";
protected static final String JMS_RESPONSE_STATUS_OK = "ok";
protected static final String JMS_RESPONSE_STATUS_PARSE_EXCEPTION = "parse_exception";
protected static final String JMS_RESPONSE_STATUS_QUERY_EXCEPTION = "query_exception";
protected static final String JMS_RESPONSE_STATUS_SERVICE_NOT_AVAILABLE_EXCEPTION = "service_not_available";
private final OLog log = Tracing.createLoggerFor(ExamPoolManagerProvider.class);
private final ExamPoolManager examPoolManager;
private TaskExecutorManager taskExecutorManager;
private ConnectionFactory connectionFactory;
private Connection connection;
private Queue examControlQueue;
private Session session;
private MessageConsumer consumer;
private final LinkedList<Session> sessions = new LinkedList<Session>();
public ExamPoolManagerProvider(ExamPoolManager examPoolManager) {
this.examPoolManager = examPoolManager;
}
@Override
public void onMessage(Message message) {
try {
log.debug("Got message : " + (message != null ? message.getClass() : "null"));
final String correlationID = message.getJMSCorrelationID();
final Destination replyTo = message.getJMSReplyTo();
if (message instanceof ObjectMessage) {
ObjectMessage objectMessage = (ObjectMessage) message;
final JMSExamMessage examMessage = (JMSExamMessage) objectMessage.getObject();
taskExecutorManager.execute(new Runnable() {
@Override
public void run() {
handleRemoteMessage(examMessage, correlationID, replyTo);
}
});
}
} catch (JMSException e) {
log.error("error when receiving jms messages", e);
} finally {
releaseSession(session);
DBFactory.getInstance().commitAndCloseSession();
}
}
private void handleRemoteMessage(JMSExamMessage examMessage, String correlationID, Destination replyTo) {
if (log.isDebug()) {
log.debug("onSearchMessage, correlationID=" + correlationID + " , replyTo=" + replyTo + " , searchRequest=" + examMessage);
}
JMSExamMessageCommand command = examMessage.getCommand();
Session localSession = null;
try {
ExamPool pool = null;
switch (command) {
case GET_EXAMPOOL: {
if (examMessage.getTestSessionId() != null) {
pool = examPoolManager.getExamPool(examMessage.getTestSessionId());
} else {
Long courseId = examMessage.getCourseId();
String nodeIdent = examMessage.getNodeIdent();
ICourse course = CourseFactory.loadCourse(courseId);
CourseNode courseNode = course.getRunStructure().getNode(nodeIdent);
pool = examPoolManager.getExamPool(course, courseNode);
}
break;
}
case CONTROLL_EXAM: {
examPoolManager.controllExam(examMessage.getTestSessionId(), examMessage.getStudents(), examMessage.getTestState());
break;
}
case ADD_STUDENT: {
Long courseId = examMessage.getCourseId();
String nodeIdent = examMessage.getNodeIdent();
ICourse course = CourseFactory.loadCourse(courseId);
CourseNode courseNode = course.getRunStructure().getNode(nodeIdent);
TestState state = examMessage.getTestState();
List<Identity> students = examMessage.getStudents();
List<QTIResultSet> results = examMessage.getResults();
Identity student = students.get(0);
QTIResultSet resultSet = results.get(0);
examPoolManager.addStudentToExamPool(course, courseNode, student, state, resultSet);
break;
}
case CHANGE_STATE: {
Long testSessionId = examMessage.getTestSessionId();
TestState state = examMessage.getTestState();
List<Identity> identities = examMessage.getStudents();
examPoolManager.changeExamState(testSessionId, identities, state);
break;
}
default:
break;
}
localSession = acquireSession();
Message responseMessage = localSession.createObjectMessage(pool);
responseMessage.setJMSCorrelationID(correlationID);
responseMessage.setStringProperty(JMS_RESPONSE_STATUS_PROPERTY_NAME, JMS_RESPONSE_STATUS_OK);
MessageProducer producer = localSession.createProducer(replyTo);
if (log.isDebug()) {
log.debug("onSearchMessage, send ResponseMessage=" + responseMessage + " to replyTo=" + replyTo);
}
producer.send(responseMessage);
producer.close();
return;
} catch (JMSException e) {
log.error("error when receiving jms messages", e);
// do not throw exceptions here throw new OLATRuntimeException();
// } catch (ServiceNotAvailableException sex) {
// sendErrorResponse(JMS_RESPONSE_STATUS_SERVICE_NOT_AVAILABLE_EXCEPTION, correlationID, replyTo);
// } catch (ParseException pex) {
// sendErrorResponse(JMS_RESPONSE_STATUS_PARSE_EXCEPTION, correlationID, replyTo);
// } catch (QueryException qex) {
// sendErrorResponse(JMS_RESPONSE_STATUS_QUERY_EXCEPTION, correlationID, replyTo);
} catch (Throwable th) {
log.error("error at ClusteredSearchProvider.receive()", th);
return;// signal search not available
// do not throw exceptions throw new OLATRuntimeException();
} finally {
releaseSession(localSession);
DBFactory.getInstance().commitAndCloseSession();
}
}
public void setConnectionFactory(ConnectionFactory conFac) {
connectionFactory = conFac;
}
public void setSearchQueue(Queue examControlQueue) {
this.examControlQueue = examControlQueue;
}
/**
* [used by Spring]
*/
public void setTaskExecutorManager(TaskExecutorManager taskExecutorManager) {
this.taskExecutorManager = taskExecutorManager;
}
public void springInit() throws JMSException {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
consumer = session.createConsumer(examControlQueue);
consumer.setMessageListener(this);
connection.start();
log.info("springInit: JMS connection started with connectionFactory=" + connectionFactory);
}
public void springStop() throws JMSException {
closeQueue();
}
private void closeQueue() {
if(consumer != null) {
try {
consumer.close();
} catch (JMSException e) {
log.error("", e);
}
}
if(connection != null) {
try {
connection.close();
} catch (JMSException e) {
log.error("", e);
}
}
}
private synchronized Session acquireSession() throws JMSException {
if (sessions.size() == 0) {
return connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
} else {
return sessions.getFirst();
}
}
private synchronized void releaseSession(Session currentSession) {
if (currentSession == null) {
return;
}
sessions.addLast(currentSession);
}
private void sendErrorResponse(String jmsResponseStatus, String correlationID, Destination replyTo) {
Session currentSession = null;
try {
currentSession = acquireSession();
Message responseMessage = currentSession.createObjectMessage();
responseMessage.setJMSCorrelationID(correlationID);
responseMessage.setStringProperty(JMS_RESPONSE_STATUS_PROPERTY_NAME, jmsResponseStatus);
MessageProducer producer = currentSession.createProducer(replyTo);
if (log.isDebug()) {
log.debug("onSearchMessage, send ResponseMessage=" + responseMessage + " to replyTo=" + replyTo);
}
producer.send(responseMessage);
producer.close();
return;
} catch (JMSException e) {
log.error("error when receiving jms messages", e);
return; //signal search not available
} finally {
releaseSession(currentSession);
}
}
}
/*
history:
$Log: ExamPoolManagerProvider.java,v $
Revision 1.4 2012-06-13 10:18:46 blaw
OLATCE-2007
OLATCE-2290
OLATCE-2189
OLATCE-1425
* improved performance and logging for exam-control
Revision 1.3 2012-04-10 13:37:02 blaw
OLATCE-1425
* more logging
Revision 1.2 2012-04-05 13:49:41 blaw
OLATCE-1425
* added history
* better indention
* refactored referencess for ExamPoolManagers to the abstract class
* added yesNoDialog for StartExam-function
* added more gui-warnings and / or fallback-values if student- or exam-values are not available
*/