com.cloudbees.sdk.CommandServiceImpl Maven / Gradle / Ivy
/*
* Copyright 2010-2013, CloudBees Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cloudbees.sdk;
import com.cloudbees.sdk.cli.*;
import com.cloudbees.sdk.extensibility.AnnotationLiteral;
import com.cloudbees.sdk.extensibility.ExtensionFinder;
import com.cloudbees.sdk.extensibility.ExtensionPointList;
import com.cloudbees.sdk.utils.Helper;
import com.google.inject.*;
import com.staxnet.appserver.utils.XmlHelper;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.xml.xpath.XPathExpressionException;
import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
*/
@Singleton
public class CommandServiceImpl implements CommandService {
private static final Logger LOGGER = Logger.getLogger(CommandServiceImpl.class.getName());
static final String NL = System.getProperty("line.separator");
@Inject
private Injector injector;
@Inject
@ExtensionClassLoader
private ClassLoader extClassLoader;
@Inject
private ExtensionPointList resolvers;
@Inject
private Verbose verbose;
DirectoryStructure structure;
List plugins = new ArrayList();
boolean localRepoLoaded;
@Inject
public CommandServiceImpl(DirectoryStructure structure) {
this.structure = structure;
}
public void loadCommandProperties() {
plugins = loadCommandFiles(structure.sdkRepository, structure.pluginExtension);
localRepoLoaded = false;
}
private ArrayList loadCommandFiles(File dir, String fileExtension) {
ArrayList plugins = new ArrayList();
String[] files = Helper.getFiles(dir, fileExtension);
if (files != null) {
for (String file : files) {
plugins.addAll(loadPlugins(new File(dir, file)));
}
}
localRepoLoaded = true;
return plugins;
}
private ArrayList loadPlugins(File commandFile) {
ArrayList plugins = new ArrayList();
try {
Plugin plugin = parsePluginFile(commandFile);
plugins.add(plugin);
} catch (Exception e) {
System.err.println("ERROR: Cannot parse file: " + commandFile);
}
return plugins;
}
private Plugin parsePluginFile(File file) throws FileNotFoundException, XPathExpressionException {
InputStream inputStream = new FileInputStream(file);
try {
InputSource input = new InputSource(inputStream);
Document doc = XmlHelper.readXML(input);
Plugin plugin = new Plugin();
Element e = doc.getDocumentElement();
if (e.getTagName().equalsIgnoreCase("plugin")) {
if (e.hasAttribute("artifact"))
plugin.setArtifact(e.getAttribute("artifact"));
NodeList nodes = e.getChildNodes();
List jars = new ArrayList();
plugin.setJars(jars);
List commands = new ArrayList();
plugin.setProperties(commands);
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node.getNodeName().equals("jar"))
jars.add(node.getTextContent().trim());
else if (node.getNodeName().equals("command")) {
CommandProperties commandProperties = new CommandProperties();
commandProperties.setGroup(getAttribute(node, "group"));
commandProperties.setName(getAttribute(node, "name"));
commandProperties.setPattern(getAttribute(node, "pattern"));
commandProperties.setDescription(getAttribute(node, "description"));
commandProperties.setClassName(getAttribute(node, "className"));
String str = getAttribute(node, "experimental");
if (str != null)
commandProperties.setExperimental(Boolean.parseBoolean(str));
str = getAttribute(node, "priority");
if (str != null)
commandProperties.setPriority(Integer.parseInt(str));
commands.add(commandProperties);
}
}
}
return plugin;
} finally {
IOUtils.closeQuietly(inputStream);
}
}
private String getAttribute(Node node, String attr) {
return getAttribute(node, attr, null);
}
private String getAttribute(Node node, String attr, String defaultValue) {
Node attrNode = node.getAttributes().getNamedItem(attr);
if (attrNode == null)
return defaultValue;
else
return attrNode.getNodeValue();
}
public ACommand getCommand(String name) throws IOException {
PluginCommand pluginCommand = getPluginCommand(name, plugins);
// Look for additional command definition in the local repository
if (pluginCommand == null) {
if (!localRepoLoaded) {
List localRepoCmds = loadCommandFiles(structure.getPluginDir(), structure.pluginExtension);
plugins.addAll(localRepoCmds);
pluginCommand = getPluginCommand(name, localRepoCmds);
}
}
ACommand command = null;
if (pluginCommand != null) {
command = getCommand(name, pluginCommand);
} else {
// Try to find the plugin via bindings
for (Plugin plugin : plugins) {
command = getInjectorCommand(name, plugin.getJars());
if (command != null) {
pluginCommand = new PluginCommand(plugin, null);
break;
}
}
}
return command;
}
public int getCount() {
return plugins.size();
}
public String getHelp(URL helpTitleFile, String groupHelp, boolean all) {
StringBuilder sb = new StringBuilder(getHelpTitle(helpTitleFile));
Map> map = new LinkedHashMap>();
if (!localRepoLoaded) plugins.addAll(loadCommandFiles(structure.getPluginDir(), structure.pluginExtension));
for (Plugin plugin : plugins) {
setPluginCommandProperties(plugin, map, all);
}
buildHelp(sb, groupHelp, map);
return sb.toString();
}
private void setPluginCommandProperties(Plugin plugin, Map> map, boolean all) {
for (CommandProperties cmd : plugin.getProperties()) {
if (cmd.getGroup() != null && (!cmd.isExperimental() || all)) {
List list = map.get(cmd.getGroup());
if (list == null) {
list = new ArrayList();
map.put(cmd.getGroup(), list);
}
list.add(cmd);
Collections.sort(list);
}
}
}
private void buildHelp(StringBuilder sb, String groupHelp, Map> map) {
for (String group : map.keySet()) {
sb.append(NL).append(group).append(" ").append(groupHelp).append(NL);
for (CommandProperties cmd : map.get(group)) {
sb.append(" ").append(Helper.getPaddedString(cmd.getName(), 30));
if (cmd.getDescription() != null)
sb.append(cmd.getDescription()).append(NL);
else
sb.append(NL);
}
}
}
public String getHelp(Plugin plugin, String groupHelp, boolean all) {
StringBuilder sb = new StringBuilder();
Map> map = new LinkedHashMap>();
setPluginCommandProperties(plugin, map, all);
buildHelp(sb, groupHelp, map);
return sb.toString();
}
public List getPlugins() {
List gavs = new ArrayList();
if (!localRepoLoaded) plugins.addAll(loadCommandFiles(structure.getPluginDir(), structure.pluginExtension));
for (Plugin plugin : plugins) {
if (plugin.getArtifact() != null)
gavs.add(new GAV(plugin.getArtifact()));
}
return gavs;
}
public Plugin getPlugin(String name) {
if (!localRepoLoaded) plugins.addAll(loadCommandFiles(structure.getPluginDir(), structure.pluginExtension));
for (Plugin plugin : plugins) {
if (plugin.getArtifact() != null) {
GAV gav = new GAV(plugin.getArtifact());
if (gav.artifactId.equalsIgnoreCase(name)) {
return plugin;
}
}
}
return null;
}
public GAV deletePlugin(String name) {
Plugin plugin = getPlugin(name);
if (plugin != null) {
GAV gav = new GAV(plugin.getArtifact());
File file = new File(structure.getPluginDir(), gav.artifactId + structure.pluginExtension);
if (file.delete()) return gav;
}
return null;
}
private StringBuffer getHelpTitle(URL helpTitleFile) {
StringBuffer sb = new StringBuffer();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(helpTitleFile.openStream()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append(NL);
}
} catch (IOException ex) {
System.err.println("ERROR: Cannot find help file: " + helpTitleFile);
} finally {
if (reader != null) try {
reader.close();
} catch (IOException ignored) {
}
}
return sb;
}
private PluginCommand getPluginCommand(String commandName, List plugins) {
PluginCommand pluginCommand = null;
for (Plugin plugin : plugins) {
for (CommandProperties commandProperties : plugin.getProperties()) {
if (commandName.matches(commandProperties.getPattern())) {
if (pluginCommand != null) {
if (commandProperties.getPriority() < pluginCommand.commandProperties.getPriority())
pluginCommand = new PluginCommand(plugin, commandProperties);
} else
pluginCommand = new PluginCommand(plugin, commandProperties);
}
}
}
return pluginCommand;
}
/**
* Look up a command from Guice,
*/
private ACommand getInjectorCommand(String name, List jars) throws IOException {
try {
Injector injector = this.injector;
if (jars != null) {
List urls = new ArrayList();
for (String jar : jars) {
urls.add(new File(jar).toURI().toURL());
}
ClassLoader pluginClassLoader = createClassLoader(urls, extClassLoader);
injector = createChildModule(injector, pluginClassLoader);
}
// CommandResolvers take precedence over our default
for (CommandResolver cr : resolvers.list(injector)) {
ACommand cmd = cr.resolve(name);
if (cmd!=null)
return cmd;
}
Provider p;
try {
p = injector.getProvider(Key.get(ACommand.class, AnnotationLiteral.of(CLICommand.class, name)));
} catch (ConfigurationException e) {
if (verbose.isVerbose()) LOGGER.log(Level.WARNING, "failed to find the command", e);
return null; // failed to find the command
}
return p.get();
} catch (Throwable e) {
throw (IOException) new IOException("Failed to resolve command: " + name).initCause(e);
}
}
/**
* Creates a classloader from all the artifacts resolved thus far.
*/
private ClassLoader createClassLoader(List urls, ClassLoader parent) {
// if (urls.isEmpty()) return parent; // nothing to load // this makes it hard to differentiate newly loaded stuff from what's already visible
return new URLClassLoader(urls.toArray(new URL[urls.size()]), parent);
}
private ACommand getCommand(String name, PluginCommand pluginCommand) throws IOException {
ACommand command;
try {
Injector injector = this.injector;
ClassLoader pluginClassLoader = this.extClassLoader;
String className = pluginCommand.commandProperties.getClassName();
List jars = pluginCommand.plugin.getJars();
if (jars != null) {
List urls = new ArrayList();
for (String jar : jars) {
urls.add(new File(jar).toURI().toURL());
}
pluginClassLoader = createClassLoader(urls, pluginClassLoader);
injector = createChildModule(injector, pluginClassLoader);
}
Provider p;
try {
Class cl = Class.forName(className, true, pluginClassLoader);
p = injector.getProvider(Key.get(cl));
} catch (ConfigurationException e) {
throw (IOException)new IOException("Failed to instantiate a command: "+name).initCause(e);
}
command = p.get();
} catch (Exception e) {
throw (IOException) new IOException("Failed to resolve command: " + name).initCause(e);
}
return command;
}
protected Injector createChildModule(Injector parent, final ClassLoader cl) throws InstantiationException, IOException {
final List childModules = new ArrayList();
childModules.add(new ExtensionFinder(cl) {
@Override
protected void bind(Class extends T> impl, Class extensionPoint) {
if (impl.getClassLoader() != cl) return; // only add newly discovered stuff
// install CLIModules
if (extensionPoint == CLIModule.class) {
try {
install((Module) impl.newInstance());
} catch (InstantiationException e) {
throw (Error) new InstantiationError().initCause(e);
} catch (IllegalAccessException e) {
throw (Error) new IllegalAccessError().initCause(e);
}
return;
}
super.bind(impl, extensionPoint);
}
});
return parent.createChildInjector(childModules);
}
class PluginCommand {
Plugin plugin;
CommandProperties commandProperties;
PluginCommand() {
}
PluginCommand(Plugin plugin, CommandProperties commandProperties) {
this.plugin = plugin;
this.commandProperties = commandProperties;
}
public Plugin getPlugin() {
return plugin;
}
public void setPlugin(Plugin plugin) {
this.plugin = plugin;
}
public CommandProperties getCommandProperties() {
return commandProperties;
}
public void setCommandProperties(CommandProperties commandProperties) {
this.commandProperties = commandProperties;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy