/*
* RHQ Management Platform
* Copyright (C) 2005-2014 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, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.enterprise.server.plugin;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.clientapi.agent.metadata.ConfigurationMetadataParser;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.cloud.Server;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.domain.plugin.PluginConfigurationRequiredException;
import org.rhq.core.domain.plugin.PluginDeploymentType;
import org.rhq.core.domain.plugin.PluginKey;
import org.rhq.core.domain.plugin.PluginStatusType;
import org.rhq.core.domain.plugin.ServerPlugin;
import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.authz.RequiredPermission;
import org.rhq.enterprise.server.cloud.instance.ServerManagerLocal;
import org.rhq.enterprise.server.plugin.pc.AbstractTypeServerPluginContainer;
import org.rhq.enterprise.server.plugin.pc.ControlResults;
import org.rhq.enterprise.server.plugin.pc.MasterServerPluginContainer;
import org.rhq.enterprise.server.plugin.pc.ServerPluginEnvironment;
import org.rhq.enterprise.server.plugin.pc.ServerPluginServiceMBean;
import org.rhq.enterprise.server.plugin.pc.ServerPluginType;
import org.rhq.enterprise.server.util.LookupUtil;
import org.rhq.enterprise.server.xmlschema.ControlDefinition;
import org.rhq.enterprise.server.xmlschema.ServerPluginDescriptorMetadataParser;
import org.rhq.enterprise.server.xmlschema.ServerPluginDescriptorUtil;
import org.rhq.enterprise.server.xmlschema.generated.serverplugin.ServerPluginDescriptorType;
/**
* A server API into the server plugin infrastructure.
*
* @author John Mazzitelli
*/
@Stateless
public class ServerPluginManagerBean implements ServerPluginManagerLocal, ServerPluginManagerRemote {
private final Log log = LogFactory.getLog(ServerPluginManagerBean.class);
@PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME)
private EntityManager entityManager;
@javax.annotation.Resource(name = "RHQ_DS", mappedName = RHQConstants.DATASOURCE_JNDI_NAME)
private DataSource dataSource;
@EJB
private ServerPluginManagerLocal serverPluginsBean; //self
@EJB
private ServerManagerLocal serverManager;
@RequiredPermission(Permission.MANAGE_SETTINGS)
public void restartMasterPluginContainer(Subject subject) {
LookupUtil.getServerPluginService().restartMasterPluginContainer();
}
@SuppressWarnings("unchecked")
public List<ServerPlugin> getServerPlugins() {
Query q = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_ALL_INSTALLED);
return q.getResultList();
}
@SuppressWarnings("unchecked")
public List<ServerPlugin> getAllServerPlugins() {
Query q = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_ALL);
return q.getResultList();
}
@Override
@SuppressWarnings("unchecked")
public List<ServerPlugin> getDeletedPlugins() {
Query q = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_DELETED);
return q.getResultList();
}
public ServerPlugin getServerPlugin(PluginKey key) {
Query query = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_BY_NAME);
query.setParameter("name", key.getPluginName());
try {
ServerPlugin plugin = (ServerPlugin) query.getSingleResult();
return plugin;
} catch (NoResultException nre) {
return null;
}
}
public ServerPlugin getServerPluginRelationships(ServerPlugin plugin) {
Query query = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_BY_NAME);
query.setParameter("name", plugin.getName());
plugin = (ServerPlugin) query.getSingleResult();
Configuration config = plugin.getPluginConfiguration();
if (config != null) {
config = entityManager.find(Configuration.class, config.getId());
plugin.setPluginConfiguration(config.deepCopy());
}
config = plugin.getScheduledJobsConfiguration();
if (config != null) {
config = entityManager.find(Configuration.class, config.getId());
plugin.setScheduledJobsConfiguration(config.deepCopy());
}
return plugin;
}
@SuppressWarnings("unchecked")
public List<ServerPlugin> getServerPluginsById(List<Integer> pluginIds) {
if (pluginIds == null || pluginIds.size() == 0) {
return new ArrayList<ServerPlugin>(); // nothing to do
}
Query query = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_BY_IDS);
query.setParameter("ids", pluginIds);
return query.getResultList();
}
@SuppressWarnings("unchecked")
public List<ServerPlugin> getEnabledServerPluginsByType(String type) {
if (type==null)
return Collections.emptyList();
Query query = entityManager.createQuery(
"SELECT sp FROM ServerPlugin sp WHERE sp.enabled=true AND sp.type LIKE :stype");
query.setParameter("stype",type);
List result = query.getResultList();
return result;
}
@SuppressWarnings("unchecked")
public List<ServerPlugin> getAllServerPluginsById(List<Integer> pluginIds) {
if (pluginIds == null || pluginIds.size() == 0) {
return new ArrayList<ServerPlugin>(); // nothing to do
}
Query query = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_ALL_BY_IDS);
query.setParameter("ids", pluginIds);
return query.getResultList();
}
public long getLastConfigurationChangeTimestamp(int pluginId) {
Query query = entityManager.createNamedQuery(ServerPlugin.QUERY_GET_CONFIG_MTIMES);
query.setParameter("id", pluginId);
Object[] timestamps = (Object[]) query.getSingleResult();
long lastConfigChangeTimestamp = 0L;
for (Object timestamp : timestamps) {
if (timestamp != null) {
long val = ((Long) timestamp).longValue();
if (val > lastConfigChangeTimestamp) {
lastConfigChangeTimestamp = val;
}
}
}
return lastConfigChangeTimestamp;
}
public ServerPluginDescriptorType getServerPluginDescriptor(PluginKey pluginKey) throws Exception {
ServerPlugin plugin = getServerPlugin(pluginKey);
if (plugin == null) {
throw new IllegalArgumentException("Unknown plugin key: " + pluginKey);
}
File pluginsDir = LookupUtil.getServerPluginService().getServerPluginsDirectory();
File pluginJar = new File(pluginsDir, plugin.getPath());
URL url = pluginJar.toURI().toURL();
ServerPluginDescriptorType descriptor = ServerPluginDescriptorUtil.loadPluginDescriptorFromUrl(url);
return descriptor;
}
@SuppressWarnings("unchecked")
public List<PluginKey> getServerPluginKeysByEnabled(boolean enabled) {
Query query = entityManager.createNamedQuery(ServerPlugin.QUERY_GET_KEYS_BY_ENABLED);
query.setParameter("enabled", Boolean.valueOf(enabled));
return query.getResultList();
}
@RequiredPermission(Permission.MANAGE_SETTINGS)
public List<PluginKey> enableServerPlugins(Subject subject, List<Integer> pluginIds) throws Exception {
if (pluginIds == null || pluginIds.size() == 0) {
return new ArrayList<PluginKey>(); // nothing to do
}
List<PluginKey> pluginKeys = new ArrayList<PluginKey>();
List<ServerPlugin> toEnable = new ArrayList<ServerPlugin>();
// first we read plugins from DB and check whether they are configured
// (such plugin would fail to load - in this case we don't enable anything and just throw PluginConfigurationRequiredException)
for (Integer pluginId : pluginIds) {
ServerPlugin plugin = null;
try {
plugin = entityManager.find(ServerPlugin.class, pluginId);
} catch (Exception e) {
log.debug("Cannot enable plugin [" + pluginId + "]. Cause: " + ThrowableUtil.getAllMessages(e));
}
if (plugin != null) {
checkForRequiredConfiguration(plugin);
toEnable.add(plugin);
}
}
for (ServerPlugin serverPlugin : toEnable) {
serverPluginsBean.setServerPluginEnabledFlag(subject, serverPlugin.getId(), true);
PluginKey pluginKey = new PluginKey(serverPlugin);
boolean success = enableServerPluginInMasterContainer(pluginKey);
if (success) {
pluginKeys.add(pluginKey);
} else {
// we can't enable the plugin in the container, there must be a problem with it - disable it
serverPluginsBean.setServerPluginEnabledFlag(subject, serverPlugin.getId(), false);
}
}
return pluginKeys;
}
/**
* checks if plugin configuration has set all required properties
* @param plugin
* @throws PluginConfigurationRequiredException if plugin is missing required configuration
* @throws Exception in case of any other error
*/
private void checkForRequiredConfiguration(ServerPlugin plugin) throws Exception {
ConfigurationDefinition configDef = getServerPluginConfigurationDefinition(new PluginKey(plugin));
Configuration configuration = plugin.getPluginConfiguration();
for (PropertyDefinition propDef : configDef.getPropertyDefinitions().values()) {
if (propDef.isRequired() && propDef instanceof PropertyDefinitionSimple) {
Property prop = configuration.get(propDef.getName());
PropertySimple simple = (PropertySimple) prop;
if (simple == null || simple.getStringValue() == null || "".equals(simple.getStringValue())) {
throw new PluginConfigurationRequiredException("Plugin [" + plugin.getDisplayName()
+ "] could not be enabled, because some required configuration fields are not set.");
}
}
}
}
private boolean enableServerPluginInMasterContainer(PluginKey pluginKey) {
ServerPluginServiceMBean serverPluginService = LookupUtil.getServerPluginService();
MasterServerPluginContainer master = serverPluginService.getMasterPluginContainer();
boolean success = true; // assume everything will be ok
if (master != null) {
AbstractTypeServerPluginContainer pc = master.getPluginContainerByPlugin(pluginKey);
if (pc != null) {
try {
pc.reloadPlugin(pluginKey, true);
try {
pc.schedulePluginJobs(pluginKey);
} catch (Exception e) {
// note that we still will report this plugin as enabled - its running
// in the plugin container, its just that we couldn't schedule its jobs
log.warn("Failed to schedule jobs for plugin [" + pluginKey + "]", e);
}
} catch (Exception e) {
log.warn("Failed to enable server plugin [" + pluginKey + "]", e);
success = false;
}
}
}
// If the master PC was not started, we will still say it was successful - the next time someone starts
// the master PC, they will be told then of any errors that might occur
return success;
}
@RequiredPermission(Permission.MANAGE_SETTINGS)
public List<PluginKey> disableServerPlugins(Subject subject, List<Integer> pluginIds) throws Exception {
if (pluginIds == null || pluginIds.size() == 0) {
return new ArrayList<PluginKey>(); // nothing to do
}
List<PluginKey> pluginKeys = new ArrayList<PluginKey>();
for (Integer pluginId : pluginIds) {
serverPluginsBean.setServerPluginEnabledFlag(subject, pluginId, false);
ServerPlugin plugin = null;
try {
plugin = entityManager.find(ServerPlugin.class, pluginId);
} catch (Exception e) {
log.debug("Cannot disable plugin [" + pluginId + "]. Cause: " + ThrowableUtil.getAllMessages(e));
}
if (plugin != null) {
PluginKey pluginKey = new PluginKey(plugin);
boolean success = disableServerPluginInMasterContainer(pluginKey);
if (success) {
pluginKeys.add(pluginKey);
}
}
}
return pluginKeys;
}
private boolean disableServerPluginInMasterContainer(PluginKey pluginKey) {
ServerPluginServiceMBean serverPluginService = LookupUtil.getServerPluginService();
MasterServerPluginContainer master = serverPluginService.getMasterPluginContainer();
boolean success = true; // assume everything will be ok
if (master != null) {
AbstractTypeServerPluginContainer pc = master.getPluginContainerByPlugin(pluginKey);
if (pc != null) {
try {
pc.unschedulePluginJobs(pluginKey);
} catch (Exception e) {
// even though we can't unschedule jobs, keep going, assume success if the PC disabled it
log.warn("Failed to unschedule jobs for plugin [" + pluginKey + "]", e);
}
try {
pc.reloadPlugin(pluginKey, false);
} catch (Exception e) {
log.warn("Failed to disable server plugin [" + pluginKey + "]", e);
success = false;
}
}
}
return success;
}
@RequiredPermission(Permission.MANAGE_SETTINGS)
public List<PluginKey> deleteServerPlugins(Subject subject, List<Integer> pluginIds) throws Exception {
if (pluginIds == null || pluginIds.size() == 0) {
return new ArrayList<PluginKey>(); // nothing to do
}
List<PluginKey> pluginKeys = new ArrayList<PluginKey>();
for (Integer pluginId : pluginIds) {
serverPluginsBean.setServerPluginEnabledFlag(subject, pluginId, false);
ServerPlugin plugin = null;
try {
plugin = entityManager.find(ServerPlugin.class, pluginId);
} catch (Exception e) {
log.debug("Cannot undeploy plugin [" + pluginId + "]. Cause: " + ThrowableUtil.getAllMessages(e));
}
if (plugin != null) {
PluginKey pluginKey = new PluginKey(plugin);
boolean success = undeployServerPluginInMasterContainer(pluginKey);
if (success) {
pluginKeys.add(pluginKey);
}
// if this plugin ever gets re-installed, let's support the use-case where the new plugin
// will have different config metadata. Here we null out the old config so the new
// config can be set to the new config definition's default values.
if (plugin.getPluginConfiguration() != null) {
entityManager.remove(plugin.getPluginConfiguration());
plugin.setPluginConfiguration(null);
}
if (plugin.getScheduledJobsConfiguration() != null) {
entityManager.remove(plugin.getScheduledJobsConfiguration());
plugin.setScheduledJobsConfiguration(null);
}
plugin.setStatus(PluginStatusType.DELETED);
// purge the file from the filesystem, we do not want this deployed again
try {
File pluginDir = LookupUtil.getServerPluginService().getServerPluginsDirectory();
File currentFile = new File(pluginDir, plugin.getPath());
currentFile.delete();
} catch (Exception e) {
log.error("Failed to delete the undeployed plugin file [" + plugin.getPath() + "]. Cause: "
+ ThrowableUtil.getAllMessages(e));
}
log.info("Server plugin [" + pluginKey + "] has been undeployed");
}
}
return pluginKeys;
}
private boolean undeployServerPluginInMasterContainer(PluginKey pluginKey) {
ServerPluginServiceMBean serverPluginService = LookupUtil.getServerPluginService();
MasterServerPluginContainer master = serverPluginService.getMasterPluginContainer();
boolean success = true; // assume everything will be ok
if (master != null) {
AbstractTypeServerPluginContainer pc = master.getPluginContainerByPlugin(pluginKey);
if (pc != null) {
try {
pc.unschedulePluginJobs(pluginKey);
} catch (Exception e) {
log.warn("Failed to unschedule jobs for server plugin [" + pluginKey + "]", e);
}
try {
pc.unloadPlugin(pluginKey);
} catch (Exception e) {
log.warn("Failed to unload server plugin [" + pluginKey + "]", e);
success = false;
}
}
}
return success;
}
@RequiredPermission(Permission.MANAGE_SETTINGS)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void setServerPluginEnabledFlag(Subject subject, int pluginId, boolean enabled) throws Exception {
Query q = entityManager.createNamedQuery(ServerPlugin.UPDATE_PLUGIN_ENABLED_BY_ID);
q.setParameter("id", pluginId);
q.setParameter("enabled", Boolean.valueOf(enabled));
q.executeUpdate();
log.info((enabled ? "Enabling" : "Disabling") + " server plugin [" + pluginId + "]");
return;
}
@RequiredPermission(Permission.MANAGE_SETTINGS)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void setServerPluginStatus(Subject subject, List<Integer> pluginIds, PluginStatusType status)
throws Exception {
if (pluginIds == null || pluginIds.size() == 0) {
return; // nothing to do
}
List<ServerPlugin> plugins = getServerPluginsById(pluginIds);
for (ServerPlugin plugin : plugins) {
plugin.setStatus(status);
updateServerPluginExceptContent(subject, plugin);
}
return;
}
@RequiredPermission(Permission.MANAGE_SETTINGS)
public ServerPlugin registerServerPlugin(Subject subject, ServerPlugin plugin, File pluginFile) throws Exception {
if (plugin.getDeployment() != PluginDeploymentType.SERVER) {
throw new IllegalArgumentException("Plugin [" + plugin.getName()
+ "] must be a server plugin to be registered");
}
PluginKey pluginKey = new PluginKey(plugin);
ServerPlugin existingPlugin = null;
boolean newOrUpdated = false;
existingPlugin = getServerPlugin(pluginKey);
if (existingPlugin == null) {
newOrUpdated = true; // this is expected for new plugins
}
if (existingPlugin != null) {
if (existingPlugin.getStatus() == PluginStatusType.DELETED) {
throw new IllegalArgumentException("Cannot register plugin [" + plugin.getName()
+ "], it has been marked as deleted");
}
ServerPlugin obsolete = ServerPluginDescriptorUtil.determineObsoletePlugin(plugin, existingPlugin);
if (obsolete == existingPlugin) { // yes use == for reference equality
// in order to keep the same configuration that the old plugin had, let's set the new plugin's config objects
// TODO: what happens if the plugin's metadata changes? we should clean the old config of old properties.
plugin.setPluginConfiguration(existingPlugin.getPluginConfiguration());
plugin.setScheduledJobsConfiguration(existingPlugin.getScheduledJobsConfiguration());
newOrUpdated = true;
}
plugin.setId(existingPlugin.getId());
}
if (newOrUpdated) {
// prepare some defaults if need be
if (plugin.getDisplayName() == null) {
plugin.setDisplayName(plugin.getName());
}
// if the ID is 0, it means this is a new plugin and we need to add a row to the db
// otherwise, this is an update to an existing plugin so we need to update the existing db row
if (plugin.getId() == 0) {
entityManager.persist(plugin);
} else {
undeployServerPluginInMasterContainer(pluginKey); // remove the old plugin to immediately; this throws away the classloader, too
plugin = updateServerPluginExceptContent(subject, plugin);
}
if (pluginFile != null) {
entityManager.flush();
streamPluginFileContentToDatabase(plugin.getId(), pluginFile);
loadServerPluginInMasterContainer(pluginFile.toURI().toURL());
}
if (plugin.isEnabled()) {
enableServerPluginInMasterContainer(pluginKey);
}
log.info("Server plugin [" + plugin.getName() + "] has been registered in the database");
}
return plugin;
}
private void loadServerPluginInMasterContainer(URL pluginUrl) throws Exception {
ServerPluginServiceMBean serverPluginService = LookupUtil.getServerPluginService();
MasterServerPluginContainer master = serverPluginService.getMasterPluginContainer();
if (master != null) {
master.loadPlugin(pluginUrl, false); // don't enable it - let the caller do that later
}
return;
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void purgeServerPlugin(int pluginId) {
// get the reference to attach to em and use the em.remove. this cascade deletes too.
ServerPlugin doomed = this.entityManager.find(ServerPlugin.class, pluginId);
doomed.getServersAcknowledgedDelete().clear();
this.entityManager.remove(doomed);
log.info("Server plugin [" + doomed + "] has been purged from the db");
return;
}
@RequiredPermission(Permission.MANAGE_SETTINGS)
public ServerPlugin updateServerPluginExceptContent(Subject subject, ServerPlugin plugin) throws Exception {
// this method is here because we need a way to update the plugin's information
// without blowing away the content data. Because we do not want to load the
// content blob in memory, the plugin's content field will be null - if we were
// to entityManager.merge that plugin POJO, it would null out that blob column.
if (plugin.getId() == 0) {
throw new IllegalArgumentException("Plugin must already exist to update it");
} else {
// make sure we create (if necessary) and attach the configs
Configuration config = plugin.getPluginConfiguration();
if (config != null) {
config = entityManager.merge(config);
plugin.setPluginConfiguration(config);
}
config = plugin.getScheduledJobsConfiguration();
if (config != null) {
config = entityManager.merge(config);
plugin.setScheduledJobsConfiguration(config);
}
ServerPlugin pluginEntity = entityManager.getReference(ServerPlugin.class, plugin.getId());
pluginEntity.setName(plugin.getName());
pluginEntity.setPath(plugin.getPath());
pluginEntity.setDisplayName(plugin.getDisplayName());
pluginEntity.setEnabled(plugin.isEnabled());
pluginEntity.setStatus(plugin.getStatus());
pluginEntity.setMd5(plugin.getMD5());
pluginEntity.setVersion(plugin.getVersion());
pluginEntity.setAmpsVersion(plugin.getAmpsVersion());
pluginEntity.setDeployment(plugin.getDeployment());
pluginEntity.setPluginConfiguration(plugin.getPluginConfiguration());
pluginEntity.setScheduledJobsConfiguration(plugin.getScheduledJobsConfiguration());
pluginEntity.setType(plugin.getType());
pluginEntity.setDescription(plugin.getDescription());
pluginEntity.setHelp(plugin.getHelp());
pluginEntity.setMtime(plugin.getMtime());
try {
entityManager.flush(); // make sure we push this out to the DB now
} catch (Exception e) {
throw new Exception("Failed to update a plugin that matches [" + plugin + "]", e);
}
}
return plugin;
}
public PluginStatusType getServerPluginStatus(PluginKey pluginKey) {
Query q = entityManager.createNamedQuery(ServerPlugin.QUERY_GET_STATUS_BY_NAME);
q.setParameter("name", pluginKey.getPluginName());
PluginStatusType status;
try {
status = (PluginStatusType) q.getSingleResult();
} catch (NoResultException nre) {
status = null; // doesn't exist in the DB, tell the caller this by returning null
}
return status;
}
@SuppressWarnings("unchecked")
public Map<ServerPluginType, List<PluginKey>> getInstalledServerPluginsGroupedByType() {
Query q = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_ALL_INSTALLED_KEYS);
List<PluginKey> keys = q.getResultList(); // all installed plugins, both enabled and disabled
Map<ServerPluginType, List<PluginKey>> allPlugins = new HashMap<ServerPluginType, List<PluginKey>>();
for (PluginKey key : keys) {
try {
ServerPluginType type = new ServerPluginType(key.getPluginType());
List<PluginKey> knownPluginsForType = allPlugins.get(type);
if (knownPluginsForType == null) {
knownPluginsForType = new ArrayList<PluginKey>();
allPlugins.put(type, knownPluginsForType);
}
knownPluginsForType.add(key);
} catch (Exception e) {
log.warn("Invalid plugin key found [" + key + "]", e);
}
}
return allPlugins;
}
@Override
public ConfigurationDefinition getServerPluginConfigurationDefinition(PluginKey pluginKey) throws Exception {
ServerPluginDescriptorType descriptor = getServerPluginDescriptor(pluginKey);
ConfigurationDefinition def;
def = ConfigurationMetadataParser.parse("pc:" + pluginKey.getPluginName(), descriptor.getPluginConfiguration());
return def;
}
@Override
public ConfigurationDefinition getServerPluginScheduledJobsDefinition(PluginKey pluginKey) throws Exception {
ServerPluginDescriptorType descriptor = getServerPluginDescriptor(pluginKey);
ConfigurationDefinition def;
def = ConfigurationMetadataParser.parse("jobs:" + pluginKey.getPluginName(), descriptor.getScheduledJobs());
return def;
}
@Override
public List<ControlDefinition> getServerPluginControlDefinitions(PluginKey pluginKey) throws Exception {
ServerPluginServiceMBean serverPluginService = LookupUtil.getServerPluginService();
MasterServerPluginContainer master = serverPluginService.getMasterPluginContainer();
if (master != null) {
AbstractTypeServerPluginContainer pc = master.getPluginContainerByPlugin(pluginKey);
if (pc != null) {
ServerPluginEnvironment env = pc.getPluginManager().getPluginEnvironment(pluginKey.getPluginName());
List<ControlDefinition> defs;
defs = ServerPluginDescriptorMetadataParser.getControlDefinitions(env.getPluginDescriptor());
return defs;
} else {
throw new Exception("There is no known plugin named [" + pluginKey + "]");
}
} else {
throw new Exception("Master plugin container not available - is it initialized?");
}
}
@Override
@RequiredPermission(Permission.MANAGE_SETTINGS)
public ControlResults invokeServerPluginControl(Subject subject, PluginKey pluginKey, String controlName,
Configuration params) throws Exception {
ServerPluginServiceMBean serverPluginService = LookupUtil.getServerPluginService();
MasterServerPluginContainer master = serverPluginService.getMasterPluginContainer();
if (master != null) {
AbstractTypeServerPluginContainer pc = master.getPluginContainerByPlugin(pluginKey);
if (pc != null) {
ControlResults results = pc.invokePluginControl(pluginKey, controlName, params);
return results;
} else {
throw new Exception("There is no known plugin named [" + pluginKey + "]. Cannot invoke [" + controlName
+ "]");
}
} else {
throw new Exception("Master plugin container not available - is it initialized?");
}
}
@Override
public boolean isReadyForPurge(int pluginId) {
ServerPlugin plugin = entityManager.find(ServerPlugin.class, pluginId);
@SuppressWarnings("unchecked")
List<Server> allServers = entityManager.createNamedQuery(Server.QUERY_FIND_ALL).getResultList();
for (Server s : allServers) {
if (!plugin.getServersAcknowledgedDelete().contains(s)) {
if (log.isDebugEnabled()) {
log.debug(plugin + " is not ready to be purged. Server " + s +
" has not acknowledged it knows about its deletion.");
}
return false;
}
}
return true;
}
@Override
public void acknowledgeDeletedPluginsBy(int serverId) {
Query q = entityManager.createNamedQuery(ServerPlugin.QUERY_UNACKED_DELETED_PLUGINS);
q.setParameter("serverId", serverId);
@SuppressWarnings("unchecked")
List<ServerPlugin> plugins = q.getResultList();
Server server = entityManager.find(Server.class, serverId);
for (ServerPlugin p : plugins) {
p.getServersAcknowledgedDelete().add(server);
entityManager.merge(p);
}
}
/**
* This will write the contents of the given plugin file to the database.
* This will assume the MD5 in the database is already correct, so this
* method will not take the time to calculate the MD5 again.
*
* @param id the ID of the plugin whose content is being updated
* @param file the plugin file whose content will be streamed to the database
*
* @throws Exception
*/
private void streamPluginFileContentToDatabase(int id, File file) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
FileInputStream fis = new FileInputStream(file);
try {
conn = this.dataSource.getConnection();
ps = conn.prepareStatement("UPDATE " + ServerPlugin.TABLE_NAME + " SET CONTENT = ? WHERE ID = ?");
BufferedInputStream bis = new BufferedInputStream(fis);
try {
ps.setBinaryStream(1, bis, (int) file.length());
ps.setInt(2, id);
int updateResults = ps.executeUpdate();
if (updateResults != 1) {
throw new Exception("Failed to update content for plugin [" + id + "] from [" + file + "]");
}
} finally {
bis.close();
}
} finally {
JDBCUtil.safeClose(conn, ps, rs);
StreamUtil.safeClose(fis);
}
return;
}
@SuppressWarnings("unchecked")
private List<PluginKey> getPluginKeysFromIds(List<Integer> pluginIds) {
Query q = entityManager.createNamedQuery(ServerPlugin.QUERY_FIND_KEYS_BY_IDS);
q.setParameter("ids", pluginIds);
List<PluginKey> keys = q.getResultList();
return keys;
}
@Override
@RequiredPermission(Permission.MANAGE_SETTINGS)
public List<ServerPlugin> getServerPlugins(Subject subject) {
return getServerPlugins();
}
}