/*
* 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-2007], 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.hqu;
import java.io.File;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.server.session.Resource;
import org.hyperic.hq.common.SystemException;
import org.hyperic.hq.context.Bootstrap;
import org.hyperic.hq.hqu.server.session.Attachment;
import org.hyperic.hq.hqu.server.session.UIPlugin;
import org.hyperic.hq.hqu.shared.UIPluginManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Service
public class RenditServerImpl implements RenditServer {
public static final String PROP_PLUGIN_NAME = "plugin.name";
public static final String PROP_PLUGIN_VER = "plugin.version";
public static final String PROP_PLUGIN_APIMAJ = "plugin.apiMajor";
public static final String PROP_PLUGIN_APIMIN = "plugin.apiMinor";
private final Log log = LogFactory.getLog(RenditServerImpl.class);
private final Object cfgLock = new Object();
private Map<String, PluginWrapper> plugins = new HashMap<String, PluginWrapper>();
private UIPluginManager uiPluginManager;
private ClassLoader getUseLoader() {
return RenditServerImpl.class.getClassLoader();
}
private PluginWrapper getPlugin(String name) {
PluginWrapper res;
synchronized (cfgLock) {
res = (PluginWrapper)plugins.get(name);
}
if (res == null) {
throw new IllegalArgumentException("Unknown plugin [" + name + "]");
}
return res;
}
@Transactional
public void addPluginDir(File path)
throws Exception
{
log.info("Loading plugin from [" + path + "]");
loadPlugin(path);
}
public void removePluginDir(String pluginName) {
log.info("Removing plugin [" + pluginName + "]");
synchronized (cfgLock) {
plugins.remove(pluginName);
}
UIPlugin plugin = uiPluginManager.findPluginByName(pluginName);
if (plugin != null){
uiPluginManager.deletePlugin(plugin);
}
}
/**
* Loads a plugin into the rendit system, verifying the version numbers,
* etc.
*
* @param path Path to the plugin
*/
public PluginWrapper loadPlugin(final File path)
throws PluginLoadException
{
try {
return loadPluginInternal(path);
} catch (Exception e) {
throw new PluginLoadException(e.getMessage(), e);
}
}
/**
* Loads a plugin into the rendit system, verifying the version numbers,
* etc.
*
* @param path Path to the plugin
*/
private PluginWrapper loadPluginInternal(final File path)
throws PluginLoadException
{
final PluginWrapper plugin = new PluginWrapper(path,
getUseLoader());
try {
plugin.loadDispatcher();
} catch(Exception e) {
throw new PluginLoadException("Failed to load plugin", e);
}
Properties props;
try {
props = plugin.loadPlugin();
} catch(PluginLoadException e) {
throw e;
} catch(Exception e) {
throw new PluginLoadException("Error loading plugin at [" +
path.getAbsolutePath() + "]", e);
}
final String pluginName = props.getProperty(PROP_PLUGIN_NAME);
final String pluginVer = props.getProperty(PROP_PLUGIN_VER);
log.info(pluginName + " version " + pluginVer +
" loaded at [" + path.getName() + "]");
deployPlugin(plugin, path, pluginName, pluginVer);
return plugin;
}
private void deployPlugin(final PluginWrapper plugin, final File path, String pluginName, String pluginVer) {
try {
// TODO Need to change this...this is done for short term to get past circular dependency w/ UIPluginManager
uiPluginManager = Bootstrap.getBean(UIPluginManager.class);
UIPlugin p = uiPluginManager.createOrUpdate(pluginName, pluginVer);
plugin.deploy(p);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
public void suspend() {
}
public void resume() {
}
public void flush() {
}
public void beforeCompletion() {
}
public void beforeCommit(boolean readOnly) {
}
public void afterCompletion(int status) {
}
public void afterCommit() {
synchronized (cfgLock) {
plugins.put(path.getName(), plugin);
}
}
});
} catch(Exception e) {
throw new PluginLoadException("Error loading HQU plugin [" +
pluginName + "]", e);
}
}
/**
* Handles regular web requests for a UI plugin.
*/
public void handleRequest(String pluginName, Object request)
throws Exception
{
PluginWrapper plugin = getPlugin(pluginName);
plugin.handleRequest(request);
}
public AttachmentDescriptor getAttachmentDescriptor(String pluginName,
Attachment a,
Resource ent,
AuthzSubject u)
{
PluginWrapper plugin = getPlugin(pluginName);
return plugin.getAttachmentDescriptor(a, ent, u);
}
public Object invokeMethod(String plugin,
InvokeMethodInvocationBindings b)
throws Exception
{
PluginWrapper p = getPlugin(plugin);
return p.invokeMethod(b);
}
/**
* Renders a template (.gsp file) to a Writer
* This facility relies on a plugin being registered under the name
* 'tmpl_render' which how to deal with a render invocation from the
* dispatcher.
*
* @params template The template to render
* @params params Local variables to pass to the template
* @params output Writer to render to
*/
public void renderTemplate(File template, Map params, Writer output)
throws Exception
{
PluginWrapper plugin = getPlugin("tmpl_render");
List args = new ArrayList();
if (plugin == null) {
throw new SystemException("Required plugin [tmpl_render] not " +
"found");
}
args.add(template);
args.add(params);
args.add(output);
InvokeMethodInvocationBindings b =
new InvokeMethodInvocationBindings("Renderer", "render", args);
plugin.invokeMethod(b);
}
}