/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to you 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 siebog.agents;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.ejb.LocalBean;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.naming.NamingException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.infinispan.Cache;
import org.jboss.resteasy.annotations.Form;
import siebog.utils.GlobalCache;
import siebog.utils.LoggerUtil;
import siebog.utils.ObjectFactory;
import siebog.utils.LoggerUtil.SocketMessageType;
/**
* Default agent manager implementation.
*
* @author <a href="mitrovic.dejan@gmail.com">Dejan Mitrovic</a>
* @author <a href="tntvteod@neobee.net">Teodor-Najdan Trifunov</a>
* @author <a href="rade.milovanovic@hotmail.com">Rade Milovanovic</a>
*/
@Stateless
@Remote(AgentManager.class)
@LocalBean
@Path("/agents")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class AgentManagerBean implements AgentManager {
private static final long serialVersionUID = 1L;
//private static final Logger LOG = LoggerFactory.getLogger(AgentManagerBean.class);
private Cache<AID, Agent> agents;
@Inject
private JndiTreeParser jndiTreeParser;
@Override
public void startServerAgent(AID aid, AgentInitArgs args) {
startServerAgent(aid, args, true);
}
@Override
public void startServerAgent(AID aid, AgentInitArgs args, boolean replace) {
if (getCache().containsKey(aid)) {
if (!replace) {
throw new IllegalStateException("Agent already running: " + aid);
}
stopAgent(aid);
if(args == null || args.get("noUIUpdate", "").equals("")) {
LoggerUtil.logAgent(aid, SocketMessageType.REMOVE);
}
}
Agent agent = null;
try {
agent = ObjectFactory.lookup(getAgentLookup(aid.getAgClass(), true), Agent.class);
} catch (IllegalStateException ex) {
agent = ObjectFactory.lookup(getAgentLookup(aid.getAgClass(), false), Agent.class);
}
initAgent(agent, aid, args);
LoggerUtil.log("Agent " + aid.getStr() + " started. AID: " + aid.toString(), true);
if(args == null || args.get("noUIUpdate", "").equals("")) {
LoggerUtil.logAgent(aid, SocketMessageType.ADD);
}
//LOG.info("Agent {} started. AID: {}", aid.getStr(), aid.toString());
}
public AID startServerAgent(AgentClass agClass, String runtimeName, AgentInitArgs args) {
return startServerAgent(agClass, runtimeName, args, true);
}
@PUT
@Path("/running/{agClass}/{name}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Override
public AID startServerAgent(@PathParam("agClass") AgentClass agClass,
@PathParam("name") String name, @Form AgentInitArgs args,
@QueryParam("replace") @DefaultValue("true") boolean replace) {
String host = AID.HOST_NAME;
if (args != null) {
host = args.get("host", AID.HOST_NAME);
}
AID aid = new AID(name, host, agClass);
startServerAgent(aid, args);
return aid;
}
@Override
public AID startClientAgent(AgentClass agClass, String name, AgentInitArgs args) {
return null;
}
@DELETE
@Path("/running/{aid}")
@Override
public void stopAgent(@PathParam("aid") AID aid) {
Agent agent = getCache().get(aid);
if (agent != null) {
getCache().remove(aid);
// agent.stop();
LoggerUtil.log("Stopped agent: " + aid, true);
LoggerUtil.logAgent(aid, SocketMessageType.REMOVE);
//LOG.info("Stopped agent: {}", aid);
}
}
@GET
@Path("/classes")
@Override
public List<AgentClass> getAvailableAgentClasses() {
try {
return jndiTreeParser.parse();
} catch (NamingException ex) {
throw new IllegalStateException(ex);
}
}
@GET
@Path("/running")
@Override
public List<AID> getRunningAgents() {
Set<AID> set = getCache().keySet();
if (set.size() > 0) {
try {
AID aid = set.iterator().next();
try {
ObjectFactory.lookup(getAgentLookup(aid.getAgClass(), true), Agent.class);
} catch (Exception ex) {
ObjectFactory.lookup(getAgentLookup(aid.getAgClass(), false), Agent.class);
}
} catch (Exception ex) {
getCache().clear();
return new ArrayList<AID>();
}
}
return new ArrayList<AID>(set);
}
@Override
public AID getAIDByRuntimeName(String runtimeName) {
// don't throw an exception if not found, because it will be intercepted
return findInRunning(runtimeName, getRunningAgents());
}
@Override
public void pingAgent(AID aid) {
try {
Agent agent = getCache().get(aid);
agent.ping();
} catch (Exception ex) {
throw new IllegalArgumentException("Unable to ping the agent.", ex);
}
}
public Agent getAgentReference(AID aid) {
// don't throw an exception here if there's no such agent
return getCache().get(aid);
}
private Cache<AID, Agent> getCache() {
if (agents == null)
agents = GlobalCache.get().getRunningAgents();
return agents;
}
private String getAgentLookup(AgentClass agClass, boolean stateful) {
if (stateful)
return String.format("ejb:/%s//%s!%s?stateful", agClass.getModule(),
agClass.getEjbName(), Agent.class.getName());
else
return String.format("ejb:/%s//%s!%s", agClass.getModule(), agClass.getEjbName(),
Agent.class.getName());
}
private void initAgent(Agent agent, AID aid, AgentInitArgs args) {
// the order of the next two statements matters. if we call init first and the agent
// sends a message from there, it sometimes happens that the reply arrives before we
// register the AID. also some agents might wish to terminate themselves inside init.
getCache().put(aid, agent);
agent.init(aid, args);
}
private AID findInRunning(String runtimeName, List<AID> running) {
for (AID aid : running) {
if (aid.getName().equals(runtimeName)) {
return aid;
}
}
return null;
}
}