/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.server.agentclient.impl;
import java.util.Date;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.clientapi.agent.bundle.BundleAgentService;
import org.rhq.core.clientapi.agent.configuration.ConfigurationAgentService;
import org.rhq.core.clientapi.agent.content.ContentAgentService;
import org.rhq.core.clientapi.agent.discovery.DiscoveryAgentService;
import org.rhq.core.clientapi.agent.drift.DriftAgentService;
import org.rhq.core.clientapi.agent.inventory.ResourceFactoryAgentService;
import org.rhq.core.clientapi.agent.lifecycle.PluginContainerLifecycle;
import org.rhq.core.clientapi.agent.measurement.MeasurementAgentService;
import org.rhq.core.clientapi.agent.operation.OperationAgentService;
import org.rhq.core.clientapi.agent.ping.PingAgentService;
import org.rhq.core.clientapi.agent.support.SupportAgentService;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.server.ExternalizableStrategy;
import org.rhq.enterprise.communications.Ping;
import org.rhq.enterprise.communications.command.Command;
import org.rhq.enterprise.communications.command.CommandResponse;
import org.rhq.enterprise.communications.command.client.ClientCommandSender;
import org.rhq.enterprise.communications.command.client.ClientRemotePojoFactory;
import org.rhq.enterprise.communications.command.client.SendCallback;
import org.rhq.enterprise.server.agentclient.AgentClient;
import org.rhq.enterprise.server.safeinvoker.HibernateDetachUtility;
/**
* Provides the mechanism by which you obtain remote interface proxies to a particular agent. Using those remote
* proxies, you can send commands to an agent.
*
* @author John Mazzitelli
*/
public class AgentClientImpl implements AgentClient {
/**
* This is the name of the command configuration property that will get assigned the security token string.
* All commands sent to the agent will get this command property set which contains the agent's token.
*/
private static final String CMDCONFIG_PROP_SECURITY_TOKEN = "rhq.security-token";
/**
* The agent that this client communicates with.
*/
private final Agent agent;
/**
* This is the underlying communications object that actually performs the sending of messages to the agent.
*/
private final ClientCommandSender sender;
/**
* This is the object that is created by the sender and is used to obtain remote proxies to the agent.
*/
private final ClientRemotePojoFactory clientRemotePojoFactory;
/**
* Constructor for {@link AgentClientImpl}.
*
* @param agent the agent that this client will communicate with
* @param sender the object that is used to send commands to the given agent
*
* @throws IllegalArgumentException if <code>agent</code> or <code>sender</code> is <code>null</code>
*/
public AgentClientImpl(Agent agent, ClientCommandSender sender) {
if (agent == null) {
throw new IllegalArgumentException("agent==null");
}
if (sender == null) {
throw new IllegalArgumentException("sender==null");
}
// Note that to decrease the footprint of this object, we create a single remote pojo invoker
// and use it to build all of our remote pojo proxies. This means that all settings within
// that one pojo invoker are set across all remote proxy invocations (unless there are annotations
// overriding those settings in the remote interfaces). It is OK if, in the future, we want to
// have one pojo invoker with one set of settings that is used to create one type of remote proxy,
// with another pojo invoker with another set of settings that is used to create another type of remote proxy.
// For now, we assume the remote pojo invoker configuration is OK for all remote proxies.
this.agent = agent;
this.sender = sender;
this.clientRemotePojoFactory = sender.getClientRemotePojoFactory();
this.sender.setSendCallbacks(new SendCallback[] { new AgentSendCallback(agent) });
// enforce the restriction (instituted in 1.1 due to multi-server HA concerns)
// that no server->agent calls use guaranteedDelivery
this.clientRemotePojoFactory.setDeliveryGuaranteed(ClientRemotePojoFactory.GuaranteedDelivery.DISABLED);
}
@Override
public Agent getAgent() {
return this.agent;
}
@Override
public String toString() {
return getAgent().toString();
}
@Override
public void startSending() {
this.sender.startSending();
}
@Override
public void stopSending() {
// Passing in false means any current commands that are queue will be aborted;
// guaranteed commands will be persisted, but any volatile commands will be lost.
// If we find that this method is called when we can't afford to lose messages
// currently queued or in-flight, think about passing in true here instead.
this.sender.stopSending(false);
}
@Override
public void updatePlugins() {
PluginContainerLifecycle lifecycle = clientRemotePojoFactory.getRemotePojo(PluginContainerLifecycle.class);
lifecycle.updatePlugins();
}
@Override
public boolean ping(long timeoutMillis) {
try {
// create our own factory so we can customize the timeout
ClientRemotePojoFactory factory = sender.getClientRemotePojoFactory();
factory.setTimeout(timeoutMillis);
Ping pinger = factory.getRemotePojo(Ping.class);
pinger.ping("", null);
return true;
} catch (Exception e) {
return false;
}
}
@Override
public boolean pingService(long timeoutMillis) {
try {
// create our own factory so we can customize the timeout
ClientRemotePojoFactory factory = sender.getClientRemotePojoFactory();
factory.setTimeout(timeoutMillis);
PingAgentService pinger = factory.getRemotePojo(PingAgentService.class);
long agentTime = pinger.ping();
long skew = Math.abs(System.currentTimeMillis() - agentTime);
if (skew > 5 * 60 * 1000L) {
LogFactory.getLog(this.getClass()).debug(
"Agent [" + agent.getName() + "] either took a long time to process a ping (" + skew
+ "ms) or its clock is not synced: " + new Date(agentTime));
}
return true;
} catch (Exception e) {
return false;
}
}
@Override
public BundleAgentService getBundleAgentService() {
return clientRemotePojoFactory.getRemotePojo(BundleAgentService.class);
}
@Override
public BundleAgentService getBundleAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(BundleAgentService.class, timeout);
}
@Override
public ContentAgentService getContentAgentService() {
return clientRemotePojoFactory.getRemotePojo(ContentAgentService.class);
}
@Override
public ContentAgentService getContentAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(ContentAgentService.class, timeout);
}
@Override
public ResourceFactoryAgentService getResourceFactoryAgentService() {
return clientRemotePojoFactory.getRemotePojo(ResourceFactoryAgentService.class);
}
@Override
public ResourceFactoryAgentService getResourceFactoryAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(ResourceFactoryAgentService.class, timeout);
}
@Override
public DiscoveryAgentService getDiscoveryAgentService() {
return clientRemotePojoFactory.getRemotePojo(DiscoveryAgentService.class);
}
@Override
public DiscoveryAgentService getDiscoveryAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(DiscoveryAgentService.class, timeout);
}
@Override
public MeasurementAgentService getMeasurementAgentService() {
return clientRemotePojoFactory.getRemotePojo(MeasurementAgentService.class);
}
@Override
public MeasurementAgentService getMeasurementAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(MeasurementAgentService.class, timeout);
}
@Override
public OperationAgentService getOperationAgentService() {
return clientRemotePojoFactory.getRemotePojo(OperationAgentService.class);
}
@Override
public OperationAgentService getOperationAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(OperationAgentService.class, timeout);
}
@Override
public ConfigurationAgentService getConfigurationAgentService() {
return clientRemotePojoFactory.getRemotePojo(ConfigurationAgentService.class);
}
@Override
public ConfigurationAgentService getConfigurationAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(ConfigurationAgentService.class, timeout);
}
@Override
public SupportAgentService getSupportAgentService() {
return clientRemotePojoFactory.getRemotePojo(SupportAgentService.class);
}
@Override
public SupportAgentService getSupportAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(SupportAgentService.class, timeout);
}
@Override
public DriftAgentService getDriftAgentService() {
return clientRemotePojoFactory.getRemotePojo(DriftAgentService.class);
}
@Override
public DriftAgentService getDriftAgentService(Long timeout) {
return clientRemotePojoFactory.getRemotePojo(DriftAgentService.class, timeout);
}
/**
* This class is used to ensure that when sending commands from Server to Agent we correctly
* set the ExternalizableStrategy to AGENT. Since the server may share threads with RemoteAPI
* processing it's possible that the thread may have a different strategy set. We serialize
* differently for the different strategies, for Agent communication we use much more lightweight
* serialization (for performance reasons).
*
* @author jshaughnessy
*/
private static class AgentSendCallback implements SendCallback {
private final String token;
public AgentSendCallback(Agent targetAgent) {
token = targetAgent.getAgentToken();
}
public void sending(Command command) {
try {
HibernateDetachUtility.nullOutUninitializedFields(command,
HibernateDetachUtility.SerializationType.SERIALIZATION);
} catch (Exception e) {
e.printStackTrace();
}
ExternalizableStrategy.setStrategy(ExternalizableStrategy.Subsystem.AGENT);
if (token != null) {
command.getConfiguration().setProperty(CMDCONFIG_PROP_SECURITY_TOKEN, token);
}
return;
}
public CommandResponse sent(Command command, CommandResponse response) {
return response;
}
}
}