Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.almende.eve.agent.Agent Maven / Gradle / Ivy
/**
* @file Agent.java
*
* @brief
* Agent is the abstract base class for all Eve agents.
* It provides basic functionality such as id, url, getting methods,
* subscribing to events, etc.
*
* @license
* 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.
*
* Copyright © 2010-2012 Almende B.V.
*
* @author Jos de Jong,
* @date 2012-12-12
*/
package com.almende.eve.agent;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.almende.eve.agent.annotation.Namespace;
import com.almende.eve.agent.callback.AsyncCallback;
import com.almende.eve.agent.callback.AsyncCallbackQueue;
import com.almende.eve.agent.callback.SyncCallback;
import com.almende.eve.event.EventsInterface;
import com.almende.eve.monitor.ResultMonitorFactoryInterface;
import com.almende.eve.rpc.RequestParams;
import com.almende.eve.rpc.annotation.Access;
import com.almende.eve.rpc.annotation.AccessType;
import com.almende.eve.rpc.annotation.Sender;
import com.almende.eve.rpc.jsonrpc.JSONMessage;
import com.almende.eve.rpc.jsonrpc.JSONRPC;
import com.almende.eve.rpc.jsonrpc.JSONRPCException;
import com.almende.eve.rpc.jsonrpc.JSONRPCException.CODE;
import com.almende.eve.rpc.jsonrpc.JSONRequest;
import com.almende.eve.rpc.jsonrpc.JSONResponse;
import com.almende.eve.rpc.jsonrpc.jackson.JOM;
import com.almende.eve.scheduler.Scheduler;
import com.almende.eve.state.State;
import com.almende.eve.state.TypedKey;
import com.almende.eve.transport.TransportService;
import com.almende.util.AnnotationUtil;
import com.almende.util.AnnotationUtil.AnnotatedClass;
import com.almende.util.TypeUtil;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* The base class for all Eve agents.
*
* @author Almende
*/
@Access(AccessType.UNAVAILABLE)
public abstract class Agent implements AgentInterface {
private static final Logger LOG = Logger.getLogger(Agent.class
.getCanonicalName());
private AgentHost host = null;
private State state = null;
private Scheduler scheduler = null;
private ResultMonitorFactoryInterface monitorFactory = null;
private EventsInterface eventsFactory = null;
private AsyncCallbackQueue callbacks = null;
private static final RequestParams EVEREQUESTPARAMS = new RequestParams();
static {
EVEREQUESTPARAMS.put(Sender.class, null);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getDescription()
*/
@Override
@Access(AccessType.PUBLIC)
public String getDescription() {
return "Base agent.";
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getVersion()
*/
@Override
@Access(AccessType.PUBLIC)
public String getVersion() {
return "1.0";
}
/**
* Instantiates a new agent.
*/
public Agent() {
}
/**
* This method is called during construction of the agent object.
* This is not a constructor itself, because else all implementing
* subclasses (=all agents) need to explicitly create this constructor.
*
* @param agentHost
* the agent host
* @param state
* the state
*/
public void constr(final AgentHost agentHost, final State state) {
if (this.state == null) {
host = agentHost;
this.state = state;
monitorFactory = agentHost.getResultMonitorFactory(this);
eventsFactory = agentHost.getEventsFactory(this);
callbacks = agentHost.getCallbackQueue(getId(),
JSONResponse.class);
// validate the Eve agent and output as warnings
final List errors = JSONRPC.validate(this.getClass(),
EVEREQUESTPARAMS);
for (final String error : errors) {
LOG.warning("Validation error class: "
+ this.getClass().getName() + ", message: " + error);
}
}
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#hasPrivate()
*/
@Override
public boolean hasPrivate() {
try {
final Class> clazz = this.getClass();
final AnnotatedClass annotated = AnnotationUtil.get(clazz);
for (final Annotation anno : annotated.getAnnotations()) {
if (anno.annotationType().equals(Access.class)
&& ((Access) anno).value() == AccessType.PRIVATE) {
return true;
}
if (anno.annotationType().equals(Sender.class)) {
return true;
}
}
} catch (final Exception e) {
LOG.log(Level.WARNING,
" Couldn't determine private annotations of agent "
+ getId(), e);
}
return false;
}
/*
* (non-Javadoc)
*
* @see
* com.almende.eve.rpc.jsonrpc.JSONAuthorizor#onAccess(java.lang.String,
* java.lang.String)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public boolean onAccess(final String senderUrl, final String functionTag) {
return true;
}
/*
* (non-Javadoc)
*
* @see
* com.almende.eve.rpc.jsonrpc.JSONAuthorizor#onAccess(java.lang.String)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public boolean onAccess(final String senderUrl) {
return onAccess(senderUrl, null);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.rpc.jsonrpc.JSONAuthorizor#isSelf(java.lang.String)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public boolean isSelf(final String senderUrl) {
if (senderUrl.startsWith("web://")) {
return true;
}
final List urls = getUrls();
return urls.contains(senderUrl);
}
/*
* (non-Javadoc)
*
* @see
* com.almende.eve.agent.AgentInterface#signalAgent(com.almende.eve.agent
* .AgentSignal)
*/
@Override
@Access(AccessType.UNAVAILABLE)
// TODO: Replace this by some form of publish/subscribe model!
public void signalAgent(final AgentSignal> event) {
if (AgentSignal.INVOKE.equals(event.getEvent())) {
onInvoke((Object[]) event.getData());
} else if (AgentSignal.RESPOND.equals(event.getEvent())) {
onRespond((JSONResponse) event.getData());
} else if (AgentSignal.RESPONSE.equals(event.getEvent())) {
onResponse((JSONResponse) event.getData());
} else if (AgentSignal.SEND.equals(event.getEvent())) {
onSend((JSONMessage) event.getData());
} else if (AgentSignal.EXCEPTION.equals(event.getEvent())) {
onException((JSONResponse) event.getData());
} else if (AgentSignal.CREATE.equals(event.getEvent())) {
onCreate();
} else if (AgentSignal.INIT.equals(event.getEvent())) {
onInit();
} else if (AgentSignal.DELETE.equals(event.getEvent())) {
onDelete();
} else if (AgentSignal.DESTROY.equals(event.getEvent())) {
onDestroy();
} else if (AgentSignal.SETSCHEDULERFACTORY.equals(event.getEvent())) {
// init scheduler tasks
scheduler = host.getScheduler(this);
} else if (AgentSignal.ADDTRANSPORTSERVICE.equals(event.getEvent())) {
final TransportService service = (TransportService) event.getData();
try {
service.reconnect(getId());
} catch (final IOException e) {
LOG.log(Level.WARNING,
"Failed to reconnect agent on new transport.", e);
}
}
}
/**
* This method is called once in the life time of an agent, at the moment
* the agent is being created by the AgentHost.
* It can be overridden and used to perform some action when the agent
* is create, in that case super.sigCreate() should be called in
* the overridden sigCreate().
*/
@Access(AccessType.UNAVAILABLE)
protected void onCreate() {
for (final TransportService service : host.getTransportServices()) {
try {
service.reconnect(getId());
} catch (final Exception e) {
LOG.log(Level.WARNING, "Couldn't reconnect transport:"
+ service + " for agent:" + getId(), e);
}
}
}
/**
* This method is called on each incoming RPC call.
* It can be overridden and used to perform some action when the agent
* is invoked.
*
* @param signalData
* the signal data
*/
@Access(AccessType.UNAVAILABLE)
protected void onInvoke(final Object[] signalData) {
}
/**
* This method is called after handling each incoming RPC call.
* It can be overridden and used to perform some action when the agent
* has been invoked.
*
* @param response
* the response
*/
@Access(AccessType.UNAVAILABLE)
protected void onRespond(final JSONResponse response) {
}
/**
* This method is called when handling any outgoing RPC messages.
* It can be overridden and used to perform some action when the agent
* is sending a message.
*
* @param message
* the message
*/
@Access(AccessType.UNAVAILABLE)
protected void onSend(final JSONMessage message) {
}
/**
* This method is called when an agent encounters any exception handling RPC
* messages.
* It can be overridden and used to perform some action when the agent
* encounters an exception.
*
* @param response
* the response
*/
@Access(AccessType.UNAVAILABLE)
protected void onException(final JSONResponse response) {
}
/**
* This method is called when handling an incoming RPC response.
* It can be overridden and used to perform some action when the agent
* has received a response.
*
* @param response
* the response
*/
@Access(AccessType.UNAVAILABLE)
protected void onResponse(final JSONResponse response) {
}
/**
* This method is called directly after the agent and its state is
* initiated.
* It can be overridden and used to perform some action when the agent
* is initialized, in that case super.sigInit() should be called in
* the overridden sigInit().
*/
@Access(AccessType.UNAVAILABLE)
protected void onInit() {
}
/**
* This method is called by the finalize method (GC) upon unloading of the
* agent from memory.
*/
@Access(AccessType.UNAVAILABLE)
protected void onDestroy() {
}
/**
* This method is called once in the life time of an agent, at the moment
* the agent is being deleted by the AgentHost.
* It can be overridden and used to perform some action when the agent
* is deleted, in that case super.sigDelete() should be called in
* the overridden sigDelete().
*/
@Access(AccessType.UNAVAILABLE)
protected void onDelete() {
// TODO: unsubscribe from all subscriptions
// cancel all scheduled tasks.
if (scheduler == null) {
scheduler = host.getScheduler(this);
}
if (scheduler != null) {
for (final String taskId : scheduler.getTasks()) {
scheduler.cancelTask(taskId);
}
}
// remove all keys from the state
// Note: the state itself will be deleted by the AgentHost
state.clear();
// save the agents class again in the state
state.put(State.KEY_AGENT_TYPE, getClass().getName());
state = null;
// forget local reference, as it can keep the State alive
// even if the AgentHost removes the file.
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#finalize()
*/
@Override
@Access(AccessType.UNAVAILABLE)
protected void finalize() throws Throwable {
// ensure the state is cleanup when the agent's method destroy is not
// called.
onDestroy();
getState().destroy();
super.finalize();
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getState()
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final State getState() {
return state;
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getScheduler()
*/
@Override
@Namespace("scheduler")
public final Scheduler getScheduler() {
if (scheduler == null) {
scheduler = host.getScheduler(this);
}
return scheduler;
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getAgentHost()
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final AgentHost getAgentHost() {
return host;
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getResultMonitorFactory()
*/
@Override
@Namespace("monitor")
public final ResultMonitorFactoryInterface getResultMonitorFactory() {
return monitorFactory;
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getEventsFactory()
*/
@Override
@Namespace("event")
public final EventsInterface getEventsFactory() {
return eventsFactory;
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getFirstUrl()
*/
@Override
@Access(AccessType.PUBLIC)
public URI getFirstUrl() {
final List urls = getUrls();
if (urls.size() > 0) {
return URI.create(urls.get(0));
}
return URI.create("local:" + getId());
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getMethods()
*/
@Override
@Access(AccessType.PUBLIC)
public List getMethods() {
return JSONRPC.describe(this, EVEREQUESTPARAMS);
}
// TODO: only allow ObjectNode as params?
/**
* Loc send.
*
* @param url
* the url
* @param method
* the method
* @param params
* the params
* @return the jSON response
* @throws IOException
* Signals that an I/O exception has occurred.
* @throws JSONRPCException
* the jSONRPC exception
*/
private JSONResponse locSend(final URI url, final String method,
final Object params) throws IOException, JSONRPCException {
ObjectNode jsonParams;
if (params instanceof ObjectNode) {
jsonParams = (ObjectNode) params;
} else {
jsonParams = JOM.getInstance().valueToTree(params);
}
// invoke the other agent via the AgentHost, allowing the factory
// to route the request internally or externally
final JSONRequest request = new JSONRequest(method, jsonParams);
final SyncCallback callback = new SyncCallback();
send(request, url, callback, null);
JSONResponse response;
try {
response = callback.get();
} catch (final Exception e) {
throw new JSONRPCException(CODE.REMOTE_EXCEPTION, "", e);
}
final JSONRPCException err = response.getError();
if (err != null) {
throw err;
}
return response;
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, java.lang.Object, java.lang.Class)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method,
final Object params, final Class type) throws IOException,
JSONRPCException {
return TypeUtil.inject(locSend(url, method, params).getResult(), type);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, java.lang.Object, java.lang.reflect.Type)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method,
final Object params, final Type type) throws IOException,
JSONRPCException {
return TypeUtil.inject(locSend(url, method, params).getResult(), type);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, java.lang.Object, com.almende.util.TypeUtil)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method,
final Object params, final TypeUtil type) throws IOException,
JSONRPCException {
return type.inject(locSend(url, method, params).getResult());
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, java.lang.Object,
* com.fasterxml.jackson.databind.JavaType)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method,
final Object params, final JavaType type) throws IOException,
JSONRPCException {
return TypeUtil.inject(locSend(url, method, params).getResult(), type);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, java.lang.reflect.Type)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method, final Type type)
throws IOException, JSONRPCException {
return TypeUtil.inject(locSend(url, method, null).getResult(), type);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, com.fasterxml.jackson.databind.JavaType)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method,
final JavaType type) throws IOException, JSONRPCException {
return TypeUtil.inject(locSend(url, method, null).getResult(), type);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, java.lang.Class)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method,
final Class type) throws IOException, JSONRPCException {
return TypeUtil.inject(locSend(url, method, null).getResult(), type);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, com.almende.util.TypeUtil)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T send(final URI url, final String method,
final TypeUtil type) throws IOException, JSONRPCException {
return type.inject(locSend(url, method, null).getResult());
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String, java.lang.Object)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void send(final URI url, final String method,
final Object params) throws IOException, JSONRPCException {
locSend(url, method, params);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.net.URI,
* java.lang.String)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void send(final URI url, final String method)
throws IOException, JSONRPCException {
locSend(url, method, null);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#createAgentProxy(java.net.URI,
* java.lang.Class)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final T createAgentProxy(final URI url,
final Class agentInterface) {
return getAgentHost().createAgentProxy(this, url, agentInterface);
}
/*
* (non-Javadoc)
*
* @see
* com.almende.eve.agent.AgentInterface#createAsyncAgentProxy(java.net.URI,
* java.lang.Class)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final AsyncProxy createAsyncAgentProxy(
final URI url, final Class agentInterface) {
return getAgentHost().createAsyncAgentProxy(this, url, agentInterface);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* java.lang.String, com.fasterxml.jackson.databind.node.ObjectNode)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final String method,
final ObjectNode params) throws IOException {
sendAsync(url, method, params, null, Void.class);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* java.lang.String)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final String method)
throws IOException {
sendAsync(url, method, null, null, Void.class);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* java.lang.String, com.fasterxml.jackson.databind.node.ObjectNode,
* com.almende.eve.agent.callback.AsyncCallback, java.lang.Class)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final String method,
final ObjectNode params, final AsyncCallback callback,
final Class type) throws IOException {
final JSONRequest request = new JSONRequest(method, params);
sendAsync(url, request, callback, JOM.getTypeFactory()
.uncheckedSimpleType(type));
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* java.lang.String, com.fasterxml.jackson.databind.node.ObjectNode,
* com.almende.eve.agent.callback.AsyncCallback, java.lang.reflect.Type)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final String method,
final ObjectNode params, final AsyncCallback callback,
final Type type) throws IOException {
final JSONRequest request = new JSONRequest(method, params);
sendAsync(url, request, callback,
JOM.getTypeFactory().constructType(type));
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* java.lang.String, com.fasterxml.jackson.databind.node.ObjectNode,
* com.almende.eve.agent.callback.AsyncCallback,
* com.fasterxml.jackson.databind.JavaType)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final String method,
final ObjectNode params, final AsyncCallback callback,
final JavaType type) throws IOException {
final JSONRequest request = new JSONRequest(method, params);
sendAsync(url, request, callback, type);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* com.almende.eve.rpc.jsonrpc.JSONRequest,
* com.almende.eve.agent.callback.AsyncCallback, java.lang.Class)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final JSONRequest request,
final AsyncCallback callback, final Class type)
throws IOException {
sendAsync(url, request, callback, JOM.getTypeFactory()
.uncheckedSimpleType(type));
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* com.almende.eve.rpc.jsonrpc.JSONRequest,
* com.almende.eve.agent.callback.AsyncCallback, java.lang.reflect.Type)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final JSONRequest request,
final AsyncCallback callback, final Type type)
throws IOException {
sendAsync(url, request, callback,
JOM.getTypeFactory().constructType(type));
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#sendAsync(java.net.URI,
* com.almende.eve.rpc.jsonrpc.JSONRequest,
* com.almende.eve.agent.callback.AsyncCallback,
* com.fasterxml.jackson.databind.JavaType)
*/
@Override
@Access(AccessType.UNAVAILABLE)
public final void sendAsync(final URI url, final JSONRequest request,
final AsyncCallback callback, final JavaType type)
throws IOException {
// Create a callback to retrieve a JSONResponse and extract the result
// or error from this. This is double nested, mostly because of the type
// conversions required on the result.
final AsyncCallback responseCallback = new AsyncCallback() {
@SuppressWarnings("unchecked")
@Override
public void onSuccess(final JSONResponse response) {
if (callback == null) {
final Exception err = response.getError();
if (err != null) {
LOG.warning("async RPC call failed, and no callback handler available:"
+ err.getLocalizedMessage());
}
} else {
final Exception err = response.getError();
if (err != null) {
callback.onFailure(err);
}
if (type != null && !type.hasRawClass(Void.class)) {
try {
final T res = (T) TypeUtil.inject(
response.getResult(), type);
callback.onSuccess(res);
} catch (final ClassCastException cce) {
callback.onFailure(new JSONRPCException(
"Incorrect return type received for JSON-RPC call:"
+ request.getMethod() + "@" + url,
cce));
}
} else {
callback.onSuccess(null);
}
}
}
@Override
public void onFailure(final Exception exception) {
if (callback == null) {
LOG.warning("async RPC call failed and no callback handler available:"
+ exception.getLocalizedMessage());
} else {
callback.onFailure(exception);
}
}
};
send(request, url, responseCallback, null);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getUrls()
*/
@Override
@Access(AccessType.PUBLIC)
public List getUrls() {
final List urls = new ArrayList();
if (host != null) {
final String agentId = getId();
for (final TransportService service : host.getTransportServices()) {
final URI url = service.getAgentUrl(agentId);
if (url != null) {
urls.add(url.toString());
}
}
urls.add("local:" + agentId);
} else {
LOG.severe("AgentHost not initialized?!?");
}
return urls;
}
/*
* (non-Javadoc)
*
* @see
* com.almende.eve.agent.AgentInterface#getRef(com.almende.eve.state.TypedKey
* )
*/
@Override
public T getRef(final TypedKey key) {
return host.getRef(getId(), key);
}
/*
* (non-Javadoc)
*
* @see
* com.almende.eve.agent.AgentInterface#putRef(com.almende.eve.state.TypedKey
* , java.lang.Object)
*/
@Override
public void putRef(final TypedKey key, final T value) {
host.putRef(getId(), key, value);
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getId()
*/
@Override
@Access(AccessType.PUBLIC)
public String getId() {
return state.getAgentId();
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#getType()
*/
@Override
@Access(AccessType.PUBLIC)
public String getType() {
return getClass().getSimpleName();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
@Access(AccessType.PUBLIC)
public String toString() {
final Map data = new HashMap();
data.put("class", this.getClass().getName());
data.put("id", getId());
return data.toString();
}
// TODO: This should be abstracted to a generic "Translation service"?
/**
* This message tries to convert/parse the given object to a JSONMessage.
* Return null if it fails to convert the message.
*
* @param msg
* the msg
* @return JSONMessage
*/
public static JSONMessage jsonConvert(final Object msg) {
JSONMessage jsonMsg = null;
try {
if (msg instanceof JSONMessage) {
jsonMsg = (JSONMessage) msg;
} else {
ObjectNode json = null;
if (msg instanceof String) {
final String message = (String) msg;
if (message.startsWith("{")
|| message.trim().startsWith("{")) {
json = JOM.getInstance().readValue(message,
ObjectNode.class);
}
} else if (msg instanceof ObjectNode) {
json = (ObjectNode) msg;
} else if (msg == null) {
LOG.warning("Message null!");
} else {
LOG.warning("Message unknown type:" + msg.getClass());
}
if (json != null) {
if (JSONRPC.isResponse(json)) {
final JSONResponse response = new JSONResponse(json);
jsonMsg = response;
} else if (JSONRPC.isRequest(json)) {
final JSONRequest request = new JSONRequest(json);
jsonMsg = request;
} else {
LOG.warning("Message contains valid JSON, but is not JSON-RPC:"
+ json);
}
}
}
} catch (Exception e) {
LOG.log(Level.WARNING,
"Message triggered exception in trying to convert it to a JSONMessage.",
e);
}
return jsonMsg;
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#receive(java.lang.Object,
* java.net.URI, java.lang.String)
*/
@Override
public void receive(final Object msg, final URI senderUrl, final String tag) {
JsonNode id = null;
try {
final JSONMessage jsonMsg = jsonConvert(msg);
if (jsonMsg != null) {
if (jsonMsg.getId() != null) {
id = jsonMsg.getId();
}
if (jsonMsg instanceof JSONRequest) {
final RequestParams params = new RequestParams();
params.put(Sender.class, senderUrl.toASCIIString());
final JSONRequest request = (JSONRequest) jsonMsg;
final AgentInterface me = this;
host.getPool().execute(new Runnable() {
@Override
public void run() {
final Object[] signalData = new Object[2];
signalData[0] = request;
signalData[1] = params;
signalAgent(new AgentSignal(
AgentSignal.INVOKE, signalData));
final JSONResponse response = JSONRPC.invoke(me,
request, params, me);
signalAgent(new AgentSignal(
AgentSignal.RESPOND, response));
try {
send(response, senderUrl, null, tag);
} catch (final IOException e) {
LOG.log(Level.WARNING, getId()
+ ": Failed to send response.", e);
}
}
});
} else if (jsonMsg instanceof JSONResponse && callbacks != null
&& id != null && !id.isNull()) {
final JSONResponse response = (JSONResponse) jsonMsg;
final AsyncCallback callback = callbacks.pull(id);
if (callback != null) {
host.getPool().execute(new Runnable() {
@Override
public void run() {
signalAgent(new AgentSignal(
AgentSignal.RESPONSE, response));
if (response.getError() != null) {
callback.onFailure(response.getError());
} else {
callback.onSuccess(response);
}
}
});
}
}
} else {
LOG.log(Level.WARNING, getId()
+ ": Received non-JSON message:'" + msg + "'");
}
} catch (final Exception e) {
LOG.log(Level.WARNING, "Exception in receiving message", e);
// generate JSON error response, skipped if it was an incoming
// notification i.s.o. request.
final JSONRPCException jsonError = new JSONRPCException(
JSONRPCException.CODE.INTERNAL_ERROR, e.getMessage(), e);
final JSONResponse response = new JSONResponse(jsonError);
response.setId(id);
signalAgent(new AgentSignal(AgentSignal.EXCEPTION,
response));
try {
send(response, senderUrl, null, tag);
} catch (final Exception e1) {
LOG.log(Level.WARNING,
getId() + ": failed to send '"
+ e.getLocalizedMessage()
+ "' error to remote agent.", e1);
}
}
}
/*
* (non-Javadoc)
*
* @see com.almende.eve.agent.AgentInterface#send(java.lang.Object,
* java.net.URI, com.almende.eve.agent.callback.AsyncCallback,
* java.lang.String)
*/
@Override
public void send(final Object msg, final URI receiverUrl,
final AsyncCallback callback, final String tag)
throws IOException {
if (msg instanceof JSONMessage) {
signalAgent(new AgentSignal(AgentSignal.SEND,
(JSONMessage) msg));
if (callback != null && callbacks != null) {
callbacks.push(((JSONMessage) msg).getId(),msg.toString(), callback);
}
}
// This should already been done!
if (msg instanceof JSONRPCException) {
LOG.log(Level.WARNING,
"Send has been called to send an JSONRPCException i.s.o. a JSONMessage...");
host.sendAsync(receiverUrl,
new JSONResponse((JSONRPCException) msg), this, tag);
return;
}
host.sendAsync(receiverUrl, msg, this, tag);
}
}