package com.ibm.jactors;
// TODO: add this to all others
/*
* Copyright (C) IBM Corportation, 2102. All rights reserved.
* Copyright (C) Barry Feigenbaum, 2102. All rights reserved.
*/
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import com.ibm.jactors.utils.Utils;
/**
* A partial implementation of an Actor for running in a DefaultActorManager.
*
* @author BFEIGENB
*
*/
public abstract class AbstractActor extends Utils implements Actor {
public static final int DEFAULT_MAX_MESSAGES = 100;
protected DefaultActorManager manager;
public ActorManager getManager() {
return manager;
}
public void setManager(DefaultActorManager manager) {
if (this.manager != null && manager != null) {
throw new IllegalStateException(
"cannot change manager of attached actor");
}
this.manager = manager;
}
protected String name;
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
if (manager != null) {
throw new IllegalStateException("cannot change name if manager set");
}
this.name = name;
}
protected String category = DEFAULT_CATEGORY;
@Override
public String getCategory() {
return category;
}
@Override
public void setCategory(String category) {
this.category = category;
}
/**
* Process a message conditionally. If testMessage() returns null no message
* will be consumed.
*
* @see AbstractActor#testMessage()
*/
@Override
public boolean receive() {
Message m = testMessage();
boolean res = m != null;
if (res) {
boolean f = remove(m);
if (!f) {
logger.warning("receive message not removed: %s", m);
}
DefaultMessage dm = (DefaultMessage) m;
try {
dm.fireMessageListeners(new MessageEvent(this, dm, MessageEvent.MessageStatus.DELIVERED));
//logger.trace("receive %s processing %s", this.getName(), m);
loopBody(m);
dm.fireMessageListeners(new MessageEvent(this, dm, MessageEvent.MessageStatus.COMPLETED));
} catch (Exception e) {
dm.fireMessageListeners(new MessageEvent(this, dm, MessageEvent.MessageStatus.FAILED));
logger.error("loop exception", e);
}
}
manager.awaitMessage(this);
return res;
}
/**
* Test to see if a message should be processed. Subclasses should override
*/
@Override
public boolean willReceive(String subject) {
return !isEmpty(subject); // default receive all subjects
}
/** Test the current message. Default action is to accept all. */
protected Message testMessage() {
return getMatch(null, false);
}
/** Process the accepted subject. */
abstract protected void loopBody(Message m);
/** Test a message against a defined subject pattern. */
protected DefaultMessage getMatch(String subject, boolean isRegExpr) {
DefaultMessage res = null;
synchronized (messages) {
res = (DefaultMessage) peekNext(subject, isRegExpr);
}
return res;
}
protected List<DefaultMessage> messages = new LinkedList<DefaultMessage>();
public DefaultMessage[] getMessages() {
return messages.toArray(new DefaultMessage[messages.size()]);
}
@Override
public int getMessageCount() {
synchronized (messages) {
return messages.size();
}
}
/**
* Limit the number of messages that can be received. Subclasses should override.
*/
@Override
public int getMaxMessageCount() {
return DEFAULT_MAX_MESSAGES;
}
/** Queue a messaged to be processed later. */
public void addMessage(DefaultMessage message) {
if (message != null) {
synchronized (messages) {
if (messages.size() < getMaxMessageCount()) {
messages.add(message);
// messages.notifyAll();
} else {
throw new IllegalStateException("too many messages, cannot add");
}
}
}
}
@Override
public Message peekNext() {
return peekNext(null);
}
@Override
public Message peekNext(String subject) {
return peekNext(subject, false);
}
/**
* See if a message exists that meets the selection criteria.
**/
@Override
public Message peekNext(String subject, boolean isRegExpr) {
Message res = null;
if (isActive) {
Pattern p = subject != null ? (isRegExpr ? Pattern.compile(subject)
: null) : null;
long now = new Date().getTime();
synchronized (messages) {
for (DefaultMessage m : messages) {
if (m.getDelayUntil() <= now) {
boolean match = subject == null
|| (isRegExpr ? m.subjectMatches(p) : m
.subjectMatches(subject));
if (match) {
res = m;
break;
}
}
}
}
}
// logger.trace("peekNext %s, %b: %s", subject, isRegExpr, res);
return res;
}
@Override
public boolean remove(Message message) {
synchronized (messages) {
return messages.remove(message);
}
}
protected boolean isActive;
public boolean isActive() {
return isActive;
}
@Override
public void activate() {
isActive = true;
}
@Override
public void deactivate() {
isActive = false;
}
/** Do startup processing. */
protected void runBody() {
DefaultMessage m = new DefaultMessage("init");
getManager().send(m, null, this);
}
@Override
public void run() {
runBody();
((DefaultActorManager) getManager()).awaitMessage(this);
}
protected boolean hasThread;
public boolean getHasThread() {
return hasThread;
}
protected void setHasThread(boolean hasThread) {
this.hasThread = hasThread;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + bodyString() + "]";
}
protected String bodyString() {
return "name=" + name + ", category=" + category + ", messages="
+ messages.size();
}
volatile protected boolean shutdown;
@Override
public boolean isShutdown() {
return shutdown;
}
@Override
public void shutdown() {
shutdown = true;
}
volatile protected boolean suspended;
@Override
public void setSuspended(boolean f) {
suspended = f;
}
@Override
public boolean isSuspended() {
return suspended;
}
}