/*
* 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, 2005, 2006], 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.product;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.agent.AgentConfig;
/**
* Deployment of embedded plugin files.
* Plugins can embed scripts, jars, native libraries, etc.
* This class deploys such files on the agent-side only to
* pdk/work/$type/$plugin/
* Where type is "scripts", "lib", etc., plugin is the plugin name.
* The pdk/$type directory must exist otherwise deployment is skipped.
*/
public class ClientPluginDeployer {
private static final Log log =
LogFactory.getLog(ClientPluginDeployer.class.getName());
private String plugin;
private String pdk;
private static HashMap handlers = new HashMap();
private static final String[] EX_DIRS = {
"scripts", "lib"
};
private static final String[] DIRS = {
"tmp"
};
static {
addHandlers(EX_DIRS, true);
addHandlers(DIRS, false);
}
public ClientPluginDeployer(String pdk, String type) {
this.pdk = pdk;
this.plugin = type;
}
public static void addHandler(String dir, boolean isExecutable) {
Handler handler = new Handler(dir);
handlers.put(handler.getName(), handler);
handler.setExecutable(isExecutable);
}
public static void addHandlers(String[] dirs,
boolean isExecutable) {
for (int i=0; i<dirs.length; i++) {
addHandler(dirs[i], isExecutable);
}
}
public Handler getHandler(String name) {
Handler handler = (Handler)handlers.get(name);
if (handler != null) {
return handler;
}
//e.g. "script" -> "scripts"
return (Handler)handlers.get(name + 's');
}
public static File getSubDirectory(String root,
String name,
String plugin) {
String subdir = "";
if (!new File(root).getName().equals(AgentConfig.WORK_DIR)) {
subdir += AgentConfig.WORK_DIR + File.separator;
}
subdir += name + File.separator + plugin;
File dir = new File(root, subdir);
if (!dir.exists()) {
if (!dir.mkdirs()) {
log.error("mkdir(" + dir + ") failed for plugin: " +
plugin);
return null;
}
else {
log.debug("mkdir(" + dir + ") succeeded for plugin: " +
plugin);
}
}
return dir;
}
public static class Handler {
private String name;
private boolean isExecutable;
Handler(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public boolean isExecutable() {
return this.isExecutable;
}
public void setExecutable(boolean value) {
this.isExecutable = value;
}
public File getSubDirectory(ClientPluginDeployer deployer) {
return ClientPluginDeployer.getSubDirectory(deployer.pdk,
this.name,
deployer.plugin);
}
}
public List unpackJar(String jar)
throws IOException {
ArrayList jars = new ArrayList();
ZipFile zipfile = new ZipFile(jar);
for (Enumeration e = zipfile.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry)e.nextElement();
String name = entry.getName();
if (entry.isDirectory()) {
continue;
}
int ix = name.indexOf('/');
if (ix == -1) {
continue;
}
String prefix = name.substring(0, ix);
name = name.substring(ix+1);
File file = getFile(prefix, name);
if (file == null) {
continue;
}
if (name.endsWith(".jar")) {
jars.add(file.toString());
}
if (upToDate(entry, file)) {
continue;
}
InputStream is = zipfile.getInputStream(entry);
try {
write(is, file);
} catch (IOException ioe) {
zipfile.close();
throw ioe;
} finally {
is.close();
}
}
zipfile.close();
return jars;
}
public class PluginFile extends File {
//shutup eclipse warning
static final long serialVersionUID = 0xff;
Handler handler;
public PluginFile(String pathname) {
super(pathname);
}
public PluginFile(File parent, String child) {
super(parent, child);
}
public PluginFile(String parent, String child) {
super(parent, child);
}
}
public boolean isDeployableType(String type) {
return getHandler(type) != null;
}
public File getFile(String type, String file) {
Handler handler = getHandler(type);
if (handler == null) {
return null;
}
File dir =
handler.getSubDirectory(this);
if (dir == null) {
log.debug("Unable to determine subdirectory to write: " +
file);
return null;
}
PluginFile pluginFile = new PluginFile(dir, file);
pluginFile.handler = handler;
return pluginFile;
}
public boolean upToDate(long source, long target) {
return source < target;
}
public boolean upToDate(ZipEntry source, File target) {
boolean upToDate =
upToDate(source.getTime(),
target.lastModified());
if (upToDate) {
log.debug("Unchanged file: " + target);
}
return upToDate;
}
public boolean upToDate(File source, File target) {
boolean upToDate =
upToDate(source.lastModified(),
target.lastModified());
if (upToDate) {
log.debug("Unchanged file: " + target);
}
return upToDate;
}
public void write(String data, File file)
throws IOException {
write(new ByteArrayInputStream(data.getBytes()), file);
}
public void write(InputStream is, File file)
throws IOException {
if (file == null) {
return;
}
boolean exists = file.exists();
FileOutputStream os;
try {
os = new FileOutputStream(file);
log.debug((exists ? "Updated" : "Created") +
" file: " + file);
} catch (IOException e) {
if (file.exists() && (file.length() > 0)) {
//e.g. on win32, agent running w/ dll loaded
//PluginDumper cannot overwrite file inuse.
return;
}
else {
String msg =
"Error writing " + file +
": " + e.getMessage();
throw new IOException(msg);
}
}
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
os.write(buffer, 0, len);
}
os.close();
if (file instanceof PluginFile) {
Handler handler = ((PluginFile)file).handler;
if (handler.isExecutable()) {
chmodx(file);
}
}
}
public boolean chmod(File file, String mode) {
if (GenericPlugin.isWin32()) {
return false;
}
try {
//XXX lame.
String cmd = "chmod " + mode + " " + file;
Process process =
Runtime.getRuntime().exec(cmd);
try {
return process.waitFor() == 0;
} catch (InterruptedException e) {
return false;
}
} catch (IOException e) {
return false;
}
}
public boolean chmodx(File file) {
return chmod(file, "+x");
}
}