Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Merge pull request #2 from zanata/add-command-hooks
Browse files Browse the repository at this point in the history
rhbz978072 Add command hooks
  • Loading branch information
seanf committed Nov 29, 2013
2 parents 98887a6 + 5900334 commit 510de92
Show file tree
Hide file tree
Showing 17 changed files with 272 additions and 13 deletions.
Expand Up @@ -189,7 +189,7 @@ Mockito.<SubCommand> anyObject())).thenReturn(
verify(mockOptions).setUrl(new URL(url));
verify(mockOptions).setProj(project);
verify(mockOptions).setProjectVersion(version);
verify(mockCommand).run();
verify(mockCommand).runWithActions();
}

/**
Expand Down
7 changes: 7 additions & 0 deletions zanata-client-commands/pom.xml
Expand Up @@ -141,5 +141,12 @@
<artifactId>ant</artifactId>
<version>1.8.2</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.1</version>
</dependency>

</dependencies>
</project>
Expand Up @@ -3,7 +3,6 @@
*/
package org.zanata.client.commands;

import java.io.PrintStream;
import java.io.PrintWriter;

import org.apache.log4j.Level;
Expand Down Expand Up @@ -46,7 +45,7 @@ public void runCommand() {
log.info("Error stacktraces are turned on.");
}
ZanataCommand cmd = opts.initCommand();
cmd.run();
cmd.runWithActions();
} catch (Exception e) {
handleException(e, opts.getErrors(), abortStrategy);
}
Expand Down
@@ -1,5 +1,11 @@
package org.zanata.client.commands;

import java.util.List;

import javax.annotation.Nonnull;

import org.zanata.client.config.CommandHook;

public interface BasicOptions {
ZanataCommand initCommand();

Expand Down Expand Up @@ -64,4 +70,7 @@ public interface BasicOptions {
*/
public String getCommandDescription();

public @Nonnull List<CommandHook> getCommandHooks();

public void setCommandHooks(@Nonnull List<CommandHook> commandHooks);
}
Expand Up @@ -20,7 +20,11 @@
*/
package org.zanata.client.commands;

import java.util.ArrayList;
import java.util.List;

import org.kohsuke.args4j.Option;
import org.zanata.client.config.CommandHook;

/**
* Base class for commands
Expand All @@ -38,6 +42,7 @@ public abstract class BasicOptionsImpl implements BasicOptions {
private boolean quiet = false;
private boolean quietSet;
private boolean interactiveMode = true;
private List<CommandHook> commandHooks = new ArrayList<CommandHook>();

public BasicOptionsImpl() {
}
Expand Down Expand Up @@ -127,4 +132,15 @@ public boolean isErrorsSet() {
public boolean isQuietSet() {
return quietSet;
}

@Override
public void setCommandHooks(List<CommandHook> commandHooks) {
this.commandHooks = commandHooks;
}

@Override
public List<CommandHook> getCommandHooks() {
return commandHooks;
}

}
Expand Up @@ -20,6 +20,16 @@
*/
package org.zanata.client.commands;

import java.util.List;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.ShutdownHookProcessDestroyer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zanata.client.config.CommandHook;
import org.zanata.rest.client.ZanataProxyFactory;

/**
Expand All @@ -35,6 +45,9 @@ public abstract class ConfigurableCommand<O extends ConfigurableOptions>
private boolean deprecated;
private String deprecationMessage;

private static final Logger log = LoggerFactory
.getLogger(ConfigurableCommand.class);

public ConfigurableCommand(O opts, ZanataProxyFactory factory) {
this.opts = opts;
if (factory != null)
Expand Down Expand Up @@ -75,4 +88,74 @@ public String getName() {
return opts.getCommandName();
}

@Override
public void runWithActions() throws Exception {
runBeforeActions();
run();
runAfterActions();
};

/**
* Runs the specific command, not including before- or after- actions.
*
* @throws Exception
*/
protected abstract void run() throws Exception;

/**
* @throws Exception
* if any of the commands fail
*/
private void runBeforeActions() throws Exception {
for (CommandHook hook : getOpts().getCommandHooks()) {
if (isForThisCommand(hook)) {
runSystemCommands(hook.getBefores());
}
}
}

/**
* @throws Exception
* if any of the commands fail.
*/
private void runAfterActions() throws Exception {
for (CommandHook hook : getOpts().getCommandHooks()) {
if (isForThisCommand(hook)) {
runSystemCommands(hook.getAfters());
}
}
}

/**
* @throws Exception
* if any of the commands fail.
*/
private void runSystemCommands(List<String> commands) throws Exception {
for (String command : commands) {
log.info("[Running command]$ " + command);
try {
DefaultExecutor executor = new DefaultExecutor();
executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
CommandLine cmdLine = CommandLine.parse(command);
int exitValue = executor.execute(cmdLine);

if (exitValue != 0) {
throw new Exception(
"Command returned non-zero exit value: "
+ exitValue);
}
log.info(" Completed with exit value: " + exitValue);
} catch (java.io.IOException e) {
throw new Exception("Failed to run command. " + e.getMessage(),
e);
} catch (InterruptedException e) {
throw new Exception("Interrupted while running command. "
+ e.getMessage(), e);
}
}
}

private boolean isForThisCommand(CommandHook hook) {
return this.getName().equals(hook.getCommand());
}
}
Expand Up @@ -3,6 +3,7 @@
import java.io.File;
import java.net.URL;


import org.kohsuke.args4j.Option;

/**
Expand Down
Expand Up @@ -2,6 +2,7 @@

import java.io.File;


import org.kohsuke.args4j.Option;
import org.zanata.client.config.LocaleList;

Expand Down Expand Up @@ -52,5 +53,4 @@ public interface ConfigurableProjectOptions extends ConfigurableOptions {
public LocaleList getLocaleMapList();

public void setLocaleMapList(LocaleList locales);

}
Expand Up @@ -90,6 +90,10 @@ private static void applyProjectConfig(ConfigurableProjectOptions opts,
}
LocaleList locales = config.getLocales();
opts.setLocaleMapList(locales);

if (opts.getCommandHooks().isEmpty() && config.getHooks() != null) {
opts.setCommandHooks(config.getHooks());
}
}

/**
Expand Down
Expand Up @@ -2,10 +2,15 @@

public interface ZanataCommand {
/**
* Executes the command, using the parameters which have been previously
* set. This method must be called after initConfig().
* Executes before-actions, the command, then after-actions. Before- and
* after-actions are specified in command hooks, and will be skipped if they
* are not supported for the command or if none are specified. The command
* is executed using the parameters which have been previously set. This
* method must be called after initConfig().
*
* @see {@link org.zanata.client.config.CommandHook}
*/
public void run() throws Exception;
public void runWithActions() throws Exception;

/**
* Returns true if the command has been deprecated.
Expand Down
@@ -0,0 +1,106 @@
/*
* Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the
* @author tags. See the copyright.txt file in the distribution for a full
* listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This software 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
* site: http://www.fsf.org.
*/
package org.zanata.client.config;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlType;

/**
* <p>Optional element used to attach system commands to run before or after a
* Zanata command (the "hooked command"). The hooked command is specified in the
* "command" attribute.</p>
*
* <p>Include {@code <hook>} elements within a {@code <hooks>} element in the
* {@code <config>} element of zanata.xml. Each command should have zero or one
* hooks. Each hook can have any number of {@code <before>} and {@code <after>}
* elements.</p>
*
* <p>Commands specified in {@code <before>} elements will be run before the hooked
* command, in the order that they are specified. Commands specified in
* {@code <after>} elements are similarly run in order after the hooked command
* successfully completes. If any command fails, including the hooked command,
* no further commands will be run.</p>
*
* e.g.
*
* <pre>
* {@code
* <hooks>
* <hook command="push">
* <before>po4a-gettextize -f man -m manpage.1 -p manpage.pot</before>
* <after>rm -f manpage.pot</after>
* </hook>
* <hook command="pull">
* <after>po4a-translate -f man -m manpage.1 -p trans/de/manpage.po -l manpage.de.1 --keep 1</after>
* <after>rm -rf trans</after>
* </hook>
* </hooks>
* }
* </pre>
*/
@XmlType(name = "hookType", propOrder = { "befores", "afters" })
public class CommandHook implements Serializable {

private static final long serialVersionUID = 1L;

private String command;
private List<String> before = new ArrayList<String>();
private List<String> after = new ArrayList<String>();

@XmlAttribute(name = "command", required = true)
public String getCommand() {
return command;
}

public void setCommand(String command) {
this.command = command;
}

@XmlElements({ @XmlElement(name = "before", type = String.class) })
public List<String> getBefores() {
return before;
}

@XmlElements({ @XmlElement(name = "after", type = String.class) })
public List<String> getAfters() {
return after;
}

@Override
public String toString() {
StringBuilder sb =
new StringBuilder("hook{ before-").append(command).append("[");
for (String bef : before) {
sb.append("\"").append(bef).append("\",");
}
sb.append("], after-").append(command).append("[");
for (String aft : after) {
sb.append("\"").append(aft).append("\",");
}
return sb.append("] }").toString();
}
}

0 comments on commit 510de92

Please sign in to comment.