/*
* Copyright (c) 2012, WSO2 Inc. (http://www.wso2.org) 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 org.apache.axis2.transport.msmq;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.axis2.AxisFault;
import org.apache.axis2.transport.base.threads.WorkerPool;
import org.apache.axis2.transport.msmq.util.IMSMQClient;
import org.apache.axis2.transport.msmq.util.MSMQCamelClient;
import org.apache.axis2.transport.msmq.util.Message;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Each service will have one ServiceTaskManager instance that will create, manage and also destroy
* idle tasks created for it, for message receipt.
*
* This also acts as the ExceptionListener for all MSMQ connections made on behalf of the service.
* Since the ExceptionListener is notified by a JMS provider on a "serious" error, we simply try
* to re-connect. Thus a connection failure for a single task, will re-initialize the state afresh
* for the service, by discarding all connections.
*/
public class ServiceTaskManager {
private static final Log log = LogFactory.getLog(ServiceTaskManager.class);
private static final int STATE_STOPPED = 0;
private static final int STATE_STARTED = 1;
private static final int STATE_PAUESED = 2;
private static final int STATE_SHUTTING_DOWN = 3;
private static final int STATE_FAILURE = 4;
private WorkerPool workerPool = null;
private volatile int activeTaskCount = 0;
private int concurrentConsumers = 1;
private String destinationQueue;
private volatile int serviceTaskManagerState = STATE_STOPPED;
private String serviceName;
private MSMQMessageReceiver msmqMessageReceiver = null;
private IMSMQClient msmqClient;
/** The list of active tasks thats managed by this instance */
private final List<MessageListenerTask> pollingTasks = Collections.synchronizedList(new ArrayList<MessageListenerTask>());
private volatile int workerState = STATE_STOPPED;
public int getServiceTaskManagerState() {
return serviceTaskManagerState;
}
public void setServiceTaskManagerState(int serviceTaskManagerState) {
this.serviceTaskManagerState = serviceTaskManagerState;
}
public MSMQMessageReceiver getMsmqMessageReceiver() {
return msmqMessageReceiver;
}
public void setMsmqMessageReceiver(MSMQMessageReceiver msmqMessageReceiver) {
this.msmqMessageReceiver = msmqMessageReceiver;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getDestinationQueue() {
return destinationQueue;
}
public void setDestinationQueue(String destinationQueue) {
this.destinationQueue = destinationQueue;
}
public IMSMQClient getMsmqClient() {
return msmqClient;
}
public void setMsmqClient(IMSMQClient msmqClient) {
this.msmqClient = msmqClient;
}
public int getActiveTaskCount() {
return activeTaskCount;
}
public void setActiveTaskCount(int activeTaskCount) {
this.activeTaskCount = activeTaskCount;
}
public int getConcurrentConsumers() {
return concurrentConsumers;
}
public void setConcurrentConsumers(int concurrentConsumers) {
this.concurrentConsumers = concurrentConsumers;
}
public WorkerPool getWorkerPool() {
return workerPool;
}
public void setWorkerPool(WorkerPool workerPool) {
this.workerPool = workerPool;
}
public synchronized void start() {
// Separate sharable thread is initiating the work under the
// MSMQLinstener
for (int i = 0; i < concurrentConsumers; i++) {
workerPool.execute(new MessageListenerTask());
}
}
/**
* Start or re-start the Task Manager by shutting down any existing worker tasks and
* re-creating them. However, if this is STM is PAUSED, a start request is ignored.
* This applies for any connection failures during paused state as well, which then will
* not try to auto recover
*/
public synchronized void stop() {
if (log.isDebugEnabled()) {
log.debug("Stopping ServiceTaskManager for service : " + serviceName);
}
if (serviceTaskManagerState != STATE_FAILURE) {
serviceTaskManagerState = STATE_SHUTTING_DOWN;
}
// TODO:implement the logic to shutdown the active resourcesrs
synchronized (pollingTasks) {
for (MessageListenerTask lstTask : pollingTasks) {
lstTask.requestShutdown();
}
}
if (serviceTaskManagerState != STATE_FAILURE) {
serviceTaskManagerState = STATE_STOPPED;
}
msmqClient = null;
synchronized (pollingTasks) {
pollingTasks.clear();
}
log.info("Task manager for service : " + serviceName + " shutdown");
}
/**
* The actual threads/tasks that perform MSMQ message polling
*/
private class MessageListenerTask implements Runnable {
private static final String ECHO = "echo";
protected void requestShutdown() {
workerState = STATE_SHUTTING_DOWN;
}
private boolean isActive() {
return workerState == STATE_STARTED;
}
/**
* As soon as we create a new polling task, add it to the STM for
* control later
*/
MessageListenerTask() {
synchronized (pollingTasks) {
pollingTasks.add(this);
}
}
public void run() {
if (destinationQueue.equals(ECHO) || serviceName.equals(ECHO)) {
return;
}
if (log.isDebugEnabled()) {
log.info("[[[MSMQ Listner has started...... MSMSQ QUEUS IS ]]]]:" + destinationQueue);
}
workerState = STATE_STARTED;
Message mqMessage = null;
msmqClient = new MSMQCamelClient();
try {
msmqClient.open(destinationQueue,org.apache.axis2.transport.msmq.util.IMSMQClient.Access.RECEIVE);
} catch (AxisFault axisFault) {
log.error("Error while opening queue!" + destinationQueue);
}
if (log.isDebugEnabled()) {
log.info("Open the destination with the name: " + destinationQueue);
}
activeTaskCount++;
if (log.isDebugEnabled()) {
log.debug("New poll task starting: thread id = " + Thread.currentThread().getId());
}
// read messages from the queue and process them for forever..
try {
while (isActive() && msmqClient != null && (getServiceName() != null && !getServiceName().isEmpty())) {
try {
mqMessage = msmqClient.receive(MSMQConstants.MSMQ_RECEIVE_TIME_OUT);
} catch (AxisFault axisFault) {
// just ignore
}
if (log.isTraceEnabled()) {
if (mqMessage != null) {
log.trace("Read a message from : + " + destinationQueue + "by Thread ID : " + Thread.currentThread().getId());
} else {
log.trace("No message received by Thread ID : " + Thread.currentThread().getId() + " for destination : " +
destinationQueue);
}
}
if (mqMessage != null) {
handleMessage(mqMessage);
}
}
} finally {
mqMessage = null;
workerState = STATE_STOPPED;
activeTaskCount--;
synchronized (pollingTasks) {
pollingTasks.remove(this);
}
}
}
}
private void handleMessage(Message msmqMessage) {
msmqMessageReceiver.onMessage(msmqMessage);
}
private void scheduleNewTaskIfAppropiate() {
// TODO
}
private void handleException(String msg, Exception e) {
log.error(msg);
throw new RuntimeException(msg, e);
}
}