package hudson.plugins.tuxdroid; import hudson.Extension; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Result; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Notifier; import hudson.tasks.Publisher; import hudson.util.FormValidation; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.net.URLEncoder; import java.rmi.ConnectException; import java.util.Iterator; import java.util.List; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import com.tuxisalive.api.TuxAPI; import com.tuxisalive.api.TuxAPIConst; import hudson.model.Hudson; /** * @author jean marc taillant */ public class TuxDroidPublisher extends Notifier { private static Object lock = new Object(); private List<String> voices; private final String tuxDroidVoice; private final String tuxDroidMacro; private final String tuxDroidSuccessTTS; private final String tuxDroidRecoverTTS; private final String tuxDroidFailTTS; private final String tuxDroidUnstableTTS; private TuxAPI tux = null; public void connect(){ try { TuxDroid.getInstance().connect(this.getDescriptor().tuxDroidUrl); } catch (Exception e) { this.log.info(e.getMessage()); } } public void disconnect(){ try { TuxDroid.getInstance().disconnect(); } catch (Exception e) { this.log.info(e.getMessage()); } } @DataBoundConstructor public TuxDroidPublisher(String reportOnSucess, String animatePenguin, String tuxDroidVoice, String tuxDroidMacro, String tuxDroidSuccessTTS, String tuxDroidRecoverTTS, String tuxDroidFailTTS, String tuxDroidUnstableTTS) { connect(); this.reportOnSucess = reportOnSucess; this.animatePenguin = animatePenguin; this.tuxDroidVoice = tuxDroidVoice; try { this.voices = this.initVoices(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } this.tuxDroidMacro = tuxDroidMacro; this.tuxDroidSuccessTTS = tuxDroidSuccessTTS; this.tuxDroidRecoverTTS = tuxDroidRecoverTTS; this.tuxDroidFailTTS = tuxDroidFailTTS; this.tuxDroidUnstableTTS = tuxDroidUnstableTTS; } private List<String> initVoices() { List<String> voices = null; try { voices = TuxDroid.getInstance().getTuxAPI().tts.getVoices(); } catch (ConnectException e) { log.info(e.getMessage()); } catch (URISyntaxException e) { log.info(e.getMessage()); } catch( NullPointerException e){ log.info(e.getMessage()); } return voices; } public List<String> getVoices() { connect(); try { this.voices = initVoices(); } catch (Exception e) { // TODO Auto-generated catch block return null; } return voices; } public String getTuxDroidSuccessTTS() { return tuxDroidSuccessTTS; } public String getTuxDroidRecoverTTS() { return tuxDroidRecoverTTS; } public String getTuxDroidFailTTS() { return tuxDroidFailTTS; } public String getTuxDroidUnstableTTS() { return tuxDroidUnstableTTS; } public String reportOnSucess = "false"; public String animatePenguin = "false"; public String getTuxDroidVoice() { return tuxDroidVoice; } public String getTuxDroidMacro() { return tuxDroidMacro; } public boolean isReportOnSucess() { if (reportOnSucess.equalsIgnoreCase("ON")) return true; return false; } public boolean isAnimatePenguin() { if (animatePenguin == null) return false; if (animatePenguin.equalsIgnoreCase("ON")) return true; return false; } /** the Logger */ private static java.util.logging.Logger log = java.util.logging.Logger .getLogger(TuxDroidPublisher.class.getName()); /** * @param message * @param earpos * @return */ private void say(final String message, BuildListener listener) { try { if (isAnimatePenguin()) TuxDroid.getInstance().getTuxAPI().mouth.onAsync(20, TuxAPIConst.SSV_CLOSE); TuxDroid.getInstance().getTuxAPI().tts.speak(message); if (isAnimatePenguin()) TuxDroid.getInstance().getTuxAPI().mouth.off(); } catch (ConnectException e) { log.info(e.getMessage()); } catch (URISyntaxException e) { log.info(e.getMessage()); } catch( NullPointerException e){ log.info(e.getMessage()); } } private void moveFlipper(BuildListener listener) { try { if (isAnimatePenguin()) TuxDroid.getInstance().getTuxAPI().flippers.onDuring(Double.valueOf(1.0), TuxAPIConst.SSV_DOWN); //TuxDroid.getInstance().getTuxAPI().flippers.waitMovingOff(Double.valueOf("3.0")); } catch (ConnectException e) { log.info(e.getMessage()); } catch (URISyntaxException e) { log.info(e.getMessage()); } catch( NullPointerException e){ log.info(e.getMessage()); } } private void changeVoice(BuildListener listener) { try { TuxDroid.getInstance().getTuxAPI().tts.setLocutor(getTuxDroidVoice()); } catch (ConnectException e) { log.info(e.getMessage()); } catch (URISyntaxException e) { log.info(e.getMessage()); } catch( NullPointerException e){ log.info(e.getMessage()); } } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.STEP; } @Override public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher, final BuildListener listener) throws InterruptedException, IOException { String msg; connect(); // Build FAILURE if ((build.getResult() == Result.FAILURE)) { msg = getTuxDroidFailTTS(); listener .getLogger() .println("TuxDroid Build will say FAILURE message"); animate(msg, build, listener); }else if (build.getResult() == Result.UNSTABLE) { // Build RECOVERY msg = getTuxDroidUnstableTTS(); listener .getLogger() .println("TuxDroid Build will say UNSTABLE message"); animate(msg, build, listener); }else if (build.getResult() == Result.SUCCESS) { // Build RECOVERY if (build.getPreviousBuild() != null && build.getPreviousBuild().getResult() == Result.FAILURE) { msg = getTuxDroidRecoverTTS(); listener .getLogger() .println("TuxDroid Build will say RECOVERY message"); animate(msg, build, listener); } // Build SUCCESS else if (isReportOnSucess()) { msg = getTuxDroidSuccessTTS(); listener .getLogger() .println("TuxDroid Build will say SUCCESS message"); animate(msg, build, listener); } else { listener .getLogger() .println( "User has choosed not to be notified of success, notification has not been sent."); } } else { listener .getLogger() .println( "Build result not handled by TuxDroid notifier, notification has not been sent."); } return true; } /** * @param message * @param earpos * @param build * @param listener * @param build */ private void animate(final String message, AbstractBuild<?, ?> build, BuildListener listener) { String substituedMessage = StringUtils.replaceEachRepeatedly(message.toUpperCase(), new String[] { "${PROJECTNAME}", "${BUILDNUMBER}", "${BUILDSTATE}"}, new String[] { build.getProject().getName(), String.valueOf(build.getNumber()), build.getResult().toString()} ); for (Iterator keyIterator = build.getBuildVariables().keySet().iterator(); keyIterator.hasNext();) { String key = (String) keyIterator.next(); substituedMessage = StringUtils.replaceEachRepeatedly(substituedMessage.toUpperCase(), new String[]{"${"+key.toUpperCase()+"}"},new String[]{build.getBuildVariableResolver().resolve(key)}); } String urlEncodedMessage = null; changeVoice(listener); moveFlipper(listener); try { urlEncodedMessage = URLEncoder.encode(substituedMessage, "UTF-8"); } catch (UnsupportedEncodingException e) { listener.getLogger().println("Unsupported Encoding "); listener.getLogger().println("Tux Droid has not been notified "); return; } say(urlEncodedMessage, listener); listener.getLogger().println(substituedMessage); listener.getLogger().println("Tux Droid has been successfully notified "); /////////////////// } @Extension public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> { public String tuxDroidUrl = "http://127.0.0.1:270"; public String tuxDroidId = "0"; public String tuxDroidVoice; public String tuxDroidSuccessTTS; public String tuxDroidRecoverTTS; public String tuxDroidMacro; public String tuxDroidFailTTS; public String tuxDroidUnstableTTS; public String reportOnSucess; public String animatePenguin; public String getTuxDroidId() { return tuxDroidId; } public String getTuxDroidUrl() { return tuxDroidUrl; } public void setTuxDroidUrl(String tuxDroidUrl) { this.tuxDroidUrl = tuxDroidUrl; } public DescriptorImpl() { load(); } @Override public boolean isApplicable(Class<? extends AbstractProject> jobType) { return true; } @Override public boolean configure(final StaplerRequest req, JSONObject json) throws FormException { tuxDroidUrl = req.getParameter("tuxDroidUrl"); save(); return true; // return super.configure(req, json); } public FormValidation doUrlCheck(@QueryParameter String tuxDroidUrl) { if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) return FormValidation.ok(); if (Util.fixEmpty(tuxDroidUrl) == null) { // hosts is not entered yet return FormValidation.ok(); } try { TuxDroid.getInstance().connect(tuxDroidUrl); TuxDroid.getInstance().disconnect(); return FormValidation.ok(); } catch (Exception e) { return FormValidation.error(e.getMessage()); } } @Override public String getDisplayName() { return "TuxDroid Publisher"; } @Override public TuxDroidPublisher newInstance(StaplerRequest req, JSONObject formData) throws FormException { // Save configuration for each trigger type this.reportOnSucess = req.getParameter("reportOnSucess"); this.animatePenguin = req.getParameter("animatePenguin"); this.tuxDroidFailTTS = req.getParameter("tuxDroidFailTTS"); this.tuxDroidMacro = req.getParameter("tuxDroidMacro"); this.tuxDroidRecoverTTS = req.getParameter("tuxDroidRecoverTTS"); this.tuxDroidSuccessTTS = req.getParameter("tuxDroidSuccessTTS"); this.tuxDroidUnstableTTS = req.getParameter("tuxDroidUnstableTTS"); this.tuxDroidVoice = req.getParameter("tuxDroidVoice"); TuxDroidPublisher m = new TuxDroidPublisher(this.reportOnSucess, this.animatePenguin, this.tuxDroidVoice, this.tuxDroidMacro, this.tuxDroidSuccessTTS, this.tuxDroidRecoverTTS, this.tuxDroidFailTTS, this.tuxDroidUnstableTTS); return m; } } }