/*
* Copyright (C) 2012 Red Hat, Inc. and/or its affiliates.
*
* 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.jboss.errai.bus.server;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.bus.client.api.messaging.MessageBus;
import org.jboss.errai.bus.client.api.RoutingFlag;
import org.jboss.errai.bus.server.service.ErraiService;
import org.slf4j.Logger;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import static java.lang.System.currentTimeMillis;
import static org.jboss.errai.bus.client.util.ErrorHelper.handleMessageDeliveryFailure;
import static org.jboss.errai.bus.client.util.ErrorHelper.sendClientError;
import static org.slf4j.LoggerFactory.getLogger;
/**
* A <tt>Worker</tt> is a specialized thread made to work with the messages and services of Errai
*/
public class Worker extends Thread {
private MessageBus bus;
private BlockingQueue<Message> messages;
private long timeout;
private volatile boolean active = true;
private volatile boolean exited = false;
private volatile long workExpiry;
private volatile Message message;
private Logger log = getLogger(this.getClass());
/**
* Initializes the thread with the specified <tt>ThreadGroup</tt>, <tt>factory</tt> and service
*
* @param factory - the factory this worker thread will belong to
* @param svc - the service the thread is attached to
*/
public Worker(WorkerFactory factory, ErraiService svc) {
super("Dispatch Worker Thread");
this.timeout = factory.getWorkerTimeout();
this.messages = factory.getMessages();
this.bus = svc.getBus();
setPriority(Thread.MIN_PRIORITY);
setDaemon(true);
}
/**
* Sets the <tt>Worker</tt> to an active or inactive state
*
* @param active - true if the thread should be active
*/
public void setActive(boolean active) {
this.active = active;
}
/**
* Returns true if this thread is valid, and hasn't expired
*
* @return false if this thread is invalid or has expired
*/
public boolean isValid() {
return workExpiry == 0 || currentTimeMillis() < workExpiry;
}
/**
* Interrupts this worker thread, and expire it due to a timeout.
* Creates an error message if they could not be interrupted
*/
public void timeoutInterrupt() {
interrupt();
if (!isInterrupted() && workExpiry != 0) {
log.warn("failed to interrupt worker:" + this.toString());
}
else {
workExpiry = 0;
sendClientError(bus, message,
"Request for '" + message.getSubject() + "' timed out.",
"The process was terminated because it exceed the maximum timeout.");
}
}
/**
* Runs the thread, setting the expiry time, and sends the messages associated with this thread
*/
@Override
public void run() {
while (true) {
try {
// looping inside a catch block is cheaper than entering and leaving it
// every time.
while (true) {
if ((message = messages.poll(60, TimeUnit.SECONDS)) != null) {
workExpiry = currentTimeMillis() + timeout;
deliverToBus(bus, message);
workExpiry = 0;
}
if (!active) {
exited = true;
return;
}
}
}
catch (InterruptedException e) {
if (!active) {
exited = true;
return;
}
}
catch (QueueUnavailableException e) {
log.debug("queue not available", e);
}
catch (Throwable e) {
message.setResource("Exception", e.getCause());
handleMessageDeliveryFailure(bus,
message, "Error calling remote service: " + message.getSubject(), e, false);
}
finally {
workExpiry = 0;
}
}
}
public static void deliverToBus(MessageBus bus, Message message) {
if (message.isFlagSet(RoutingFlag.NonGlobalRouting)) {
bus.send(message);
}
else {
bus.sendGlobal(message);
}
}
public boolean isStopped() {
return exited;
}
}