/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2008], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.agent.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.hyperic.hq.agent.AgentConnectionException;
import org.hyperic.hq.agent.AgentRemoteException;
import org.hyperic.hq.agent.FileData;
import org.hyperic.hq.agent.FileDataResult;
import org.hyperic.hq.appdef.Agent;
import org.hyperic.hq.transport.AgentProxyFactory;
import org.hyperic.hq.transport.util.InputStreamServiceImpl;
import org.hyperic.hq.transport.util.RemoteInputStream;
/**
* The Agent Commands client that uses the new transport.
*/
public class AgentCommandsClientImpl
extends AbstractCommandsClient implements AgentCommandsClient {
private final boolean _agentRegistrationClient;
public AgentCommandsClientImpl(Agent agent, AgentProxyFactory factory) {
super(agent, factory);
_agentRegistrationClient = false;
}
/**
* This constructor should only be used during agent registration where
* the agent doesn't yet know its agent token and the Agent pojo has not
* yet been persisted on the server.
*/
public AgentCommandsClientImpl(AgentProxyFactory factory,
String agentAddress,
int agentPort,
boolean unidirectional) {
super(createAgent(agentAddress, agentPort, unidirectional), factory);
_agentRegistrationClient = true;
}
private static Agent createAgent(String agentAddress,
int agentPort,
boolean unidirectional) {
Agent agent = new Agent();
agent.setAddress(agentAddress);
agent.setPort(agentPort);
agent.setUnidirectional(unidirectional);
return agent;
}
/**
* @see org.hyperic.hq.agent.client.AgentCommandsClient#agentSendFileData(org.hyperic.hq.agent.FileData[], java.io.InputStream[])
*/
public FileDataResult[] agentSendFileData(FileData[] destFiles,
InputStream[] streams)
throws AgentRemoteException, AgentConnectionException {
assertOnlyPingsAllowedForAgentRegistration();
if(destFiles.length != streams.length){
throw new IllegalArgumentException("Streams and dest files " +
"arrays must be the same " +
"length");
}
InputStreamServiceImpl streamService = InputStreamServiceImpl.getInstance();
RemoteInputStream remoteIs = streamService.getRemoteStream();
AgentCommandsClient proxy = null;
try {
// must be an async proxy so the current thread is not blocked from
// sending the file stream bytes to the remote client
proxy = (AgentCommandsClient)getAsynchronousProxy(AgentCommandsClient.class, false);
proxy.agentSendFileData(destFiles, new InputStream[]{remoteIs});
} finally {
safeDestroyService(proxy);
}
OutputStream outStream = null;
try {
outStream = new OutputStreamWrapper(streamService, remoteIs.getStreamId());
// Send the file to the agent in 32kb chunks
FileStreamMultiplexer muxer = new FileStreamMultiplexer(32*1024);
return muxer.sendData(outStream, destFiles, streams);
} catch(IOException exc){
throw new AgentRemoteException("IO Exception while sending " +
"file data: " + exc.getMessage(), exc);
} finally {
if (outStream != null) {
try {
outStream.close();
} catch (IOException e) {
// swallow
}
}
}
}
private static class OutputStreamWrapper extends OutputStream {
private final InputStreamServiceImpl _streamService;
private final String _streamId;
public OutputStreamWrapper(InputStreamServiceImpl streamService, String streamId) {
_streamService = streamService;
_streamId = streamId;
}
public void write(byte b[], int off, int len) throws IOException {
// Always copy the buffer since the invoking thread may be
// reusing this buffer.
byte[] buffer = new byte[len-off];
System.arraycopy(b, off, buffer, 0, buffer.length);
try {
_streamService.writeBufferToRemoteStream(_streamId, buffer);
} catch (InterruptedException e) {
throw new IOException("buffer write interrupted");
}
}
public void write(int b) throws IOException {
throw new UnsupportedOperationException("single byte writes not supported");
}
public void close() throws IOException {
boolean eosSignaled = false;
int i = 0;
while (!eosSignaled && i++ < 10) {
try {
_streamService.signalEndOfRemoteStream(_streamId);
eosSignaled = true;
} catch (InterruptedException e) {
}
}
}
}
/**
* @see org.hyperic.hq.agent.client.AgentCommandsClient#die()
*/
public void die() throws AgentRemoteException, AgentConnectionException {
assertOnlyPingsAllowedForAgentRegistration();
AgentCommandsClient proxy = null;
try {
proxy = (AgentCommandsClient)getAsynchronousProxy(AgentCommandsClient.class, false);
proxy.die();
} finally {
safeDestroyService(proxy);
}
}
/**
* @see org.hyperic.hq.agent.client.AgentCommandsClient#ping()
*/
public long ping() throws AgentRemoteException, AgentConnectionException {
if (_agentRegistrationClient && getAgent().isUnidirectional()) {
// The unidirectional client does not work yet since the agent
// is not aware of its agent token at this time
return 0;
}
AgentCommandsClient proxy = null;
try {
proxy = (AgentCommandsClient)getSynchronousProxy(AgentCommandsClient.class);
long sendTime = System.currentTimeMillis();
proxy.ping();
long recvTime = System.currentTimeMillis();
return (recvTime-sendTime);
} finally {
safeDestroyService(proxy);
}
}
/**
* @see org.hyperic.hq.agent.client.AgentCommandsClient#getCurrentAgentBundle()
*/
public String getCurrentAgentBundle()
throws AgentRemoteException, AgentConnectionException {
AgentCommandsClient proxy = null;
try {
proxy = (AgentCommandsClient)getSynchronousProxy(AgentCommandsClient.class);
return proxy.getCurrentAgentBundle();
} finally {
safeDestroyService(proxy);
}
}
/**
* @see org.hyperic.hq.agent.client.AgentCommandsClient#upgrade(java.lang.String, java.lang.String)
*/
public Map upgrade(String tarFile, String destination)
throws AgentRemoteException, AgentConnectionException {
AgentCommandsClient proxy = null;
Map result = new HashMap();
try {
proxy = (AgentCommandsClient)getSynchronousProxy(AgentCommandsClient.class);
result = proxy.upgrade(tarFile, destination);
} finally {
safeDestroyService(proxy);
}
return result;
}
/**
* @see org.hyperic.hq.agent.client.AgentCommandsClient#restart()
*/
public void restart() throws AgentRemoteException, AgentConnectionException {
assertOnlyPingsAllowedForAgentRegistration();
AgentCommandsClient proxy = null;
try {
proxy = (AgentCommandsClient)getAsynchronousProxy(AgentCommandsClient.class, false);
proxy.restart();
} finally {
safeDestroyService(proxy);
}
}
private void assertOnlyPingsAllowedForAgentRegistration()
throws AgentConnectionException {
if (_agentRegistrationClient) {
throw new AgentConnectionException("Only client ping is allowed");
}
}
public Map<String, Boolean> agentRemoveFile(Collection<String> files)
throws AgentRemoteException, AgentConnectionException {
AgentCommandsClient proxy = null;
try {
proxy = (AgentCommandsClient)getAsynchronousProxy(AgentCommandsClient.class, false);
Map<String, Boolean> rtn = proxy.agentRemoveFile(files);
if (rtn == null) {
_log.error("error removing files from agent=" + getAgent() + " files=" + files);
rtn = new HashMap<String, Boolean>(files.size());
for (String file : files) {
rtn.put(file, false);
}
}
return rtn;
} finally {
safeDestroyService(proxy);
}
}
}