Life gets in the way... but we're back with our final installment! So where to start, let's start with a publisher for freestyle builds, then we'll add a publisher for Maven 2 builds... These will both require some reports to display results, and then finally we'll need the plugin entry point. But before we get into all that, perhaps I should briefly explain structured form submission support

DataBoundConstructors

Hudson uses Stapler as it's web framework. One of the things that Stapler provides is support for constructing objects from a JSON data model. Basically, if you have a class with a public constructor annotated with @DataBoundConstructor, Stapler will bind fields from a JSON object by matching the field name to the constructor parameter name. If a parameter also has a @DataBoundConstructor, then Stapler will recurse to construct this child object from the child JSON object.

Note: The only hole in this (at the moment) is if you want to inject a variable class, i.e. it does not support the case where there are three ChildImpl classes all implementing Child, and all with @DataBoundConstructor and Parent's constructor has a parameter which takes Child... However, plans are afoot to fix this!

JavaNCSSPublisher

Publishers in Hudson must have a Descriptor, this will be registered with Hudson and allows Hudson to create Publisher instances which have the details for the project they are publishing. Descriptors are normally implemented as an inner class called DescriptorImpl and there is normally a static field of the publisher DESCRIPTOR that holds the Descriptor singleton. 99.995% of the time, you will want your publisher to have a @DataBoundConstructor, so without further delay, here is the publisher:

package hudson.plugins.javancss;

import hudson.maven.MavenModule;
import hudson.maven.MavenModuleSet;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Descriptor;
import hudson.plugins.helpers.AbstractPublisherImpl;
import hudson.plugins.helpers.Ghostwriter;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Publisher;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

public class JavaNCSSPublisher extends AbstractPublisherImpl {

private String reportFilenamePattern;

@DataBoundConstructor
public JavaNCSSPublisher(String reportFilenamePattern) {
reportFilenamePattern.getClass();
this.reportFilenamePattern = reportFilenamePattern;
}

public String getReportFilenamePattern() {
return reportFilenamePattern;
}

public boolean needsToRunAfterFinalized() {
return false;
}

public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

public Descriptor<Publisher> getDescriptor() {
return DESCRIPTOR;
}

public Action getProjectAction(AbstractProject<?, ?> project) {
return new JavaNCSSProjectIndividualReport(project);
}

protected Ghostwriter newGhostwriter() {
return new JavaNCSSGhostwriter(reportFilenamePattern);
}

public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {

private DescriptorImpl() {
super(JavaNCSSPublisher.class);
}

public String getDisplayName() {
return "Publish " + PluginImpl.DISPLAY_NAME;
}

public boolean isApplicable(Class<? extends AbstractProject> aClass) {
return !MavenModuleSet.class.isAssignableFrom(aClass)
&& !MavenModule.class.isAssignableFrom(aClass);
}

}

}

By inheriting from AbstractPublisherImpl we get a lot of the work done for us, all we really need to do is provide a Ghostwriter and the project level report (JavaNCSSProjectIndividualReport which we will see later

We need hudson/plugins/javancss/JavaNCSSPublisher/config.jelly to allow the user to specify the report file name pattern... not much to this, so here it is:

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:bh="/lib/health">
<f:entry title="JavaNCSS xml report pattern"
description="
This is a file name pattern that can be used to locate the JavaNCSS xml report files
(for example with Maven2 use &lt;b&gt;**/target/javancss-raw-report.xml&lt;/b&gt;).
The path is relative to &lt;a href='ws/'&gt;the module root&lt;/a&gt; unless
you are using Subversion as SCM and have configured multiple modules, in which case it is
relative to the workspace root.&lt;br/&gt;
JavaNCSS must be configured to generate XML reports for this plugin to function.
">
<f:textbox name="javancss.reportFilenamePattern" value="${instance.reportFilenamePattern}"/>
</f:entry>
</j:jelly>

JavaNCSSMavenPublisher

This is fairly similar to the freestyle publisher, except that we do not need the user to configure everything for us, as we can grab some of the stuff from the pom.xml.

We could, if necessary, tweak the pom.xml to ensure that the report we are looking for is generated... an example of this is the cobertura maven plugin which does not generate an XML report by default. A Hudson plugin can modify the cobertura plugin's configuration before it executes in order to ensure that the xml report is generated. Note: some people regard this kind of thing as evil, as the pom.xml is no longer behaving the same as when run from the command line.

Ok, so here is the Maven publisher...

package hudson.plugins.javancss;

import hudson.maven.*;
import hudson.model.Action;
import hudson.plugins.helpers.AbstractMavenReporterImpl;
import hudson.plugins.helpers.Ghostwriter;
import net.sf.json.JSONObject;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

import java.io.File;

public class JavaNCSSMavenPublisher extends AbstractMavenReporterImpl {

@DataBoundConstructor
public JavaNCSSMavenPublisher() {
}

private static final String PLUGIN_GROUP_ID = "org.codehaus.mojo";
private static final String PLUGIN_ARTIFACT_ID = "javancss-maven-plugin";
private static final String PLUGIN_EXECUTE_GOAL = "report";

protected boolean isExecutingMojo(MojoInfo mojo) {
return mojo.pluginName.matches(PLUGIN_GROUP_ID, PLUGIN_ARTIFACT_ID)
&& PLUGIN_EXECUTE_GOAL.equals(mojo.getGoal());
}

protected Ghostwriter newGhostwriter(MavenProject pom, MojoInfo mojo) {
// get the name of the xml report

String tempFileName;
try {
tempFileName = mojo.getConfigurationValue("tempFileName", String.class);
} catch (ComponentConfigurationException e) {
tempFileName = null;
}
if (tempFileName == null) {
// the name was not overridden in the pom, so use the default
tempFileName = "javancss-raw-report.xml";
}

// get the xml output directory

File baseDir = pom.getBasedir().getAbsoluteFile();
File xmlOutputDirectory;
try {
xmlOutputDirectory = mojo.getConfigurationValue("xmlOutputDirector", File.class);
} catch (ComponentConfigurationException e) {
xmlOutputDirectory = null;
}
if (xmlOutputDirectory == null) {
// the directory was not overridden in the pom, so use the default
xmlOutputDirectory = new File(pom.getBuild().getDirectory());
}

String searchPath;
String targetPath = makeDirEndWithFileSeparator(fixFilePathSeparator(xmlOutputDirectory.getAbsolutePath()));
String baseDirPath = makeDirEndWithFileSeparator(fixFilePathSeparator(baseDir.getAbsolutePath()));

if (targetPath.startsWith(baseDirPath)) {
searchPath = targetPath.substring(baseDirPath.length()) + tempFileName;
} else {
// we have no clue where this is, so default to anywhere
searchPath = "**/" + tempFileName;
}

return new JavaNCSSGhostwriter(searchPath, targets);
}

private String makeDirEndWithFileSeparator(String baseDirPath) {
if (!baseDirPath.endsWith(File.separator)) {
baseDirPath += File.separator;
}
return baseDirPath;
}

private String fixFilePathSeparator(String path) {
return path.replace(File.separatorChar == '/' ? '\\' : '/', File.separatorChar);
}

public Action getProjectAction(MavenModule module) {
for (MavenBuild build : module.getBuilds()) {
if (build.getAction(JavaNCSSBuildIndividualReport.class) != null) {
return new JavaNCSSProjectIndividualReport(module);
}
}
return null;
}

public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

/**
* {@inheritDoc}
*/
public MavenReporterDescriptor getDescriptor() {
return DESCRIPTOR; //To change body of implemented methods use File | Settings | File Templates.
}

public static final class DescriptorImpl extends MavenReporterDescriptor {

/**
* Do not instantiate DescriptorImpl.
*/
private DescriptorImpl() {
super(JavaNCSSMavenPublisher.class);
}

/**
* {@inheritDoc}
*/
public String getDisplayName() {
return "Publish " + PluginImpl.DISPLAY_NAME;
}

public MavenReporter newInstance(StaplerRequest req, JSONObject formData) throws FormException {
return req.bindJSON(JavaNCSSMavenPublisher.class, formData);
}

}

}

The only complexity is in the newGhostwriter method, where we have to find out what the configuration for the maven plugin is in order to find the xml report.

We will need a hudson/plugins/javancss/JavaNCSSMavenPublisher/config.jelly file for this publisher... not much to this one as we get what we need from the pom.xml

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:bh="/lib/health">
</j:jelly>

The reports

We have a total of four reports to generate:

  • Individual build report - used for freestyle and maven 2 modules
  • Individual project report - used for freestyle and maven 2 modules
  • Aggregated build report - used for maven 2 projects
  • Aggregated project report - used for maven 2 projects

To keep to our DRY principals, we'll use some abstract classes to pull together the common code. First, AbstractBuildReport which will form the basis of our build reports:

package hudson.plugins.javancss;

import hudson.model.AbstractBuild;
import hudson.plugins.helpers.AbstractBuildAction;
import hudson.plugins.javancss.parser.Statistic;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import java.io.IOException;
import java.util.Collection;

public abstract class AbstractBuildReport<T extends AbstractBuild<?, ?>> extends AbstractBuildAction<T> {
private final Collection<Statistic> results;
private final Statistic totals;

public AbstractBuildReport(Collection<Statistic> results) {
this.results = results;
this.totals = Statistic.total(results);
}

public Collection<Statistic> getResults() {
return results;
}

public Statistic getTotals() {
return totals;
}

public String getSummary() {
AbstractBuild<?, ?> prevBuild = getBuild().getPreviousBuild();
while (prevBuild != null && prevBuild.getAction(getClass()) == null) {
prevBuild = prevBuild.getPreviousBuild();
}
if (prevBuild == null) {
return totals.toSummary();
} else {
AbstractBuildReport action = prevBuild.getAction(getClass());
return totals.toSummary(action.getTotals());
}
}

public String getIconFileName() {
return PluginImpl.ICON_FILE_NAME;
}

public String getDisplayName() {
return PluginImpl.DISPLAY_NAME;
}

public String getUrlName() {
return PluginImpl.URL;
}

public boolean isGraphActive() {
return false;
}

}

Similarly, we have AbstractProjectReport which will be used for project reports:

package hudson.plugins.javancss;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;

import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.ProminentProjectAction;
import hudson.plugins.helpers.AbstractProjectAction;
import hudson.plugins.javancss.parser.Statistic;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

public abstract class AbstractProjectReport<T extends AbstractProject<?, ?>>
extends AbstractProjectAction<T>
implements ProminentProjectAction {

public AbstractProjectReport(T project) {
super(project);
}

public String getIconFileName() {
for (AbstractBuild<?, ?> build = getProject().getLastBuild();
build != null; build = build.getPreviousBuild()) {

final AbstractBuildReport action = build.getAction(getBuildActionClass());
if (action != null) {
return PluginImpl.ICON_FILE_NAME;
}
}
return null;
}

public String getDisplayName() {
for (AbstractBuild<?, ?> build = getProject().getLastBuild();
build != null; build = build.getPreviousBuild()) {
final AbstractBuildReport action = build.getAction(getBuildActionClass());
if (action != null) {
return PluginImpl.DISPLAY_NAME;
}
}
return null;
}

public String getUrlName() {
for (AbstractBuild<?, ?> build = getProject().getLastBuild(); build != null; build = build.getPreviousBuild()) {
final AbstractBuildReport action = build.getAction(getBuildActionClass());
if (action != null) {
return PluginImpl.URL;
}
}
return null;
}

public String getSearchUrl() {
return PluginImpl.URL;
}

public boolean isGraphActive() {
return false;
}

public Collection<Statistic> getResults() {
for (AbstractBuild<?, ?> build = getProject().getLastBuild();
build != null; build = build.getPreviousBuild()) {
final AbstractBuildReport action = build.getAction(getBuildActionClass());
if (action != null) {
return action.getResults();
}
}
return Collections.emptySet();
}

public Statistic getTotals() {
for (AbstractBuild<?, ?> build = getProject().getLastBuild();
build != null; build = build.getPreviousBuild()) {
final AbstractBuildReport action = build.getAction(getBuildActionClass());
if (action != null) {
return action.getTotals();
}
}
return null;
}

protected abstract Class<? extends AbstractBuildReport> getBuildActionClass();

}

Now that we have these abstract classes, we can roll out our concrete reports. First the individual build report:

package hudson.plugins.javancss;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import hudson.maven.AggregatableAction;
import hudson.maven.MavenAggregatedReport;
import hudson.maven.MavenBuild;
import hudson.maven.MavenModule;
import hudson.maven.MavenModuleSetBuild;
import hudson.model.AbstractBuild;
import hudson.plugins.javancss.parser.Statistic;

public class JavaNCSSBuildIndividualReport extends AbstractBuildReport<AbstractBuild<?, ?>>
implements AggregatableAction {

public JavaNCSSBuildIndividualReport(Collection<Statistic> results) {
super(results);
}

@Override
public synchronized void setBuild(AbstractBuild<?, ?> build) {
super.setBuild(build);
if (this.getBuild() != null) {
for (Statistic r : getResults()) {
r.setOwner(this.getBuild());
}
}
}

public MavenAggregatedReport createAggregatedAction(MavenModuleSetBuild build,
Map<MavenModule,
List<MavenBuild>> moduleBuilds) {
return new JavaNCSSBuildAggregatedReport(build, moduleBuilds);
}

}

That was fairly painless... Note that we interfaces for both the freestyle and maven2 project types, this is OK as the freestyle projects will ignore the Maven2 stuff and vice-versa while the common code is shared by both. Next we need the aggregated build report:

package hudson.plugins.javancss;

import hudson.maven.*;
import hudson.model.Action;
import hudson.plugins.javancss.parser.Statistic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class JavaNCSSBuildAggregatedReport
extends AbstractBuildReport<MavenModuleSetBuild>
implements MavenAggregatedReport {

public JavaNCSSBuildAggregatedReport(MavenModuleSetBuild build,
Map<MavenModule, List<MavenBuild>> moduleBuilds) {
super(new ArrayList<Statistic>());
setBuild(build);
}

public synchronized void update(Map<MavenModule, List<MavenBuild>> moduleBuilds,
MavenBuild newBuild) {
JavaNCSSBuildIndividualReport report =
newBuild.getAction(JavaNCSSBuildIndividualReport.class);

if (report != null) {
Collection<Statistic> u = Statistic.merge(report.getResults(), getResults());
getResults().clear();
getResults().addAll(u);
getTotals().add(report.getTotals());
}
}

public Class<? extends AggregatableAction> getIndividualActionType() {
return JavaNCSSBuildIndividualReport.class;
}

public Action getProjectAction(MavenModuleSet moduleSet) {
for (MavenModuleSetBuild build : moduleSet.getBuilds()) {
if (build.getAction(JavaNCSSBuildAggregatedReport.class) != null) {
return new JavaNCSSProjectAggregatedReport(moduleSet);
}
}
return null;
}

}

This report is only used for the Maven2 project types. The two key methods are update which is called as each module completes, and getProjectAction which should return the project level aggregated report if there is a report to show. At this point we're ready for the individual project report:

package hudson.plugins.javancss;

import hudson.model.AbstractProject;
import hudson.model.Actionable;
import hudson.model.ProminentProjectAction;
import hudson.model.AbstractBuild;
import hudson.util.ChartUtil;
import hudson.util.DataSetBuilder;
import hudson.plugins.javancss.parser.Statistic;

import java.util.Collection;

public class JavaNCSSProjectIndividualReport
extends AbstractProjectReport<AbstractProject<?, ?>>
implements ProminentProjectAction {

public JavaNCSSProjectIndividualReport(AbstractProject<?, ?> project) {
super(project);
}

protected Class<? extends AbstractBuildReport> getBuildActionClass() {
return JavaNCSSBuildIndividualReport.class;
}
}

Don't repeat ourselves comes in handy here as essentially all the work has been done for us!. The project aggregated report:

package hudson.plugins.javancss;

import hudson.model.Actionable;
import hudson.model.ProminentProjectAction;
import hudson.model.AbstractBuild;
import hudson.model.Action;
import hudson.maven.MavenModuleSet;
import hudson.maven.MavenModuleSetBuild;
import hudson.plugins.javancss.parser.Statistic;

public class JavaNCSSProjectAggregatedReport
extends AbstractProjectReport<MavenModuleSet>
implements ProminentProjectAction {

public JavaNCSSProjectAggregatedReport(MavenModuleSet project) {
super(project);
}

protected Class<? extends AbstractBuildReport> getBuildActionClass() {
return JavaNCSSBuildAggregatedReport.class;
}
}

Again DRY to the rescue... At this point all that remains is to present the reports from these backing objects... so on with the jelly views. The helper classes and our inheritance makes this easy... all we need is two jelly files: hudson/plugins/javancss/AbstractBuildReport/reportDetail.jelly and hudson/plugins/javancss/AbstractProjectReport/reportDetail.jelly. Here they are:

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<h1>Results</h1>

<table border="1px" class="pane sortable">
<thead>
<tr>
<th>Package</th>
<th title="Class count">Classes</th>
<th title="Function count">Functions</th>
<th title="Javadoc count">Javadocs</th>
<th title="Non-commenting Source Statements">NCSS</th>
<th title="Javadoc line count">JLC</th>
<th title="Single-line comment line count">SLCLC</th>
<th title="Multi-line comment line count">MLCLC</th>
</tr>
</thead>
<tfoot>
<tr>
<th align="left">Totals</th>
<th align="right">${it.totals.classes}</th>
<th align="right">${it.totals.functions}</th>
<th align="right">${it.totals.javadocs}</th>
<th align="right">${it.totals.ncss}</th>
<th align="right">${it.totals.javadocLines}</th>
<th align="right">${it.totals.singleCommentLines}</th>
<th align="right">${it.totals.multiCommentLines}</th>
</tr>
</tfoot>
<tbody>
<j:forEach var="r" items="${it.results}">
<tr>
<th align="left">${r.name}</th>
<td align="right">${r.classes}</td>
<td align="right">${r.functions}</td>
<td align="right">${r.javadocs}</td>
<td align="right">${r.ncss}</td>
<td align="right">${r.javadocLines}</td>
<td align="right">${r.singleCommentLines}</td>
<td align="right">${r.multiCommentLines}</td>
</tr>
</j:forEach>
</tbody>
</table>
</j:jelly>

Yep, the two files are identical! Other plugins may not be quite so lucky... but in general the project level report should be the same as the report for the latest build

Making a plugin

Now we are ready to make our plugin.... for this we need a class that extends hudson.Plugin and registers our publisher's descriptors with the appropriate lists... here it is:

package hudson.plugins.javancss;

import hudson.Plugin;
import hudson.maven.MavenReporters;
import hudson.tasks.BuildStep;

public class PluginImpl extends Plugin {
public void start() throws Exception {
BuildStep.PUBLISHERS.add(JavaNCSSPublisher.DESCRIPTOR);
MavenReporters.LIST.add(JavaNCSSMavenPublisher.DESCRIPTOR);
}

public static String DISPLAY_NAME = "Java NCSS Report";
public static String GRAPH_NAME = "Java NCSS Trend";
public static String URL = "javancss";
public static String ICON_FILE_NAME = "graph.gif";
}

And that's pretty much it... we should have a working plugin

Finishing touches

OK, so the plugin does not have health reports (i.e. the weather icons) and it does not show a trend graph... I think I'm going to need a part 8 :-(

8

View comments

Another interesting tidbit:

@Benchmark

public void debug1Arg_date(Blackhole blackhole) {

LOGGER.debug("Started at %s", new Date(time));

time++;

blackhole.consume(time);

}

@Benchmark

public void guardedDebug1Arg_date(Blackhole blackhole) {

if (LOGGER.isDebugEnabled()) {

LOGGER.debug("Starte

One of my colleagues had a question:

Wondering what is worse: concatenation of strings for a log, or creating an array to pass those params through and use placeholders #JUL

— Baptiste Mathus (@bmathus)

December 5, 2016

Now for some context, when the Jenkins project started off, Kohsuke was work

If there are two only things guaranteed to cause all out war between developers, my vote is that those two things are:

Code formatting conventions Version number conventions The first is resolved trivially (just shoot anyone suggesting the use of TABs).

One of the headline grabbing things that people seem to be raving about over Log4J 2.0 is async loggers.

Now do not get me wrong, they are great… as long as you know what you are doing.

We have all had the bad technical managers… the ones who tell you to go and do something… and then micromanage the task they have “delegated” to you.

They are a pain to work for.

One of the qualities of a good manager is to tailor their delegation to the people they are delegating to.

There are two ways to build a Maven project with Jenkins*

Use a free-style project with a Maven build step Use a Maven-style project The first way runs the build as Maven intended. The second way adds a whole lot of hooks and can even modify the build in ways that Maven did not intend.

6

I was given a link to yet another article preaching about how 100% code coverage is the only goal (the article is preaching, not Tatu BTW)

Oh what a bunch of baloney: centare.com/100-code-cover… -- at least for any project I've ever worked on, anywhere.

For use when you have multiple JVM providers (Apple & Oracle), you want to be able to switch between JDKs for each CLI

usejava () { local sel=$1.jdk if [ -x "/Library/Java/JavaVirtualMachines/jdk$sel/Contents/Home/bin/java" -a ! -x "/Library/Java/JavaVirtualMachines/$1/Contents/Home/bin/java" ] the

Special Symbols and Characters on the regular Mac Keyboard

These are for the British Keyboard layout 

Currency Symbols

Trademark and Copyright Symbols

Apple Symbol

Math and Greek Character Symbols

Copyediting, typesetting, and miscellaneous symbols

Punctuation marks

This is a repost of my post on the CloudBees Developers blog

TL;DR Source control injection attacks are a bigger worry than build tool injection attacks, and if you cannot trust your local filesystem, then you cannot trust anything.

Hamlet: ... for there is nothing either good or bad, but thinking makes it so.

Hamlet Act 2, scene 2, 239–251

William Shakespeare

The Apache Software Foundation is a meritocracy. By this we mean that you gain status based on the merit of your work and actions.

3

With Java Generics, it can be useful to insert wildcards wherever possible, but how do you decide which wildcard is correct? When do you use ? super T, ? extends T and when should you not use a wildcard at all.

Cmd+Shift+4 followed by Space is a lovely way to get a screen shot of a window on an Apple Mac...

1

Java has a couple of “nice” features, specifically:

Built-in locks Baked in support for object serialization Now I don't want to get into a war about how both of these are flawed (there was a reason that I put “nice” in quotes) but here is a quick note on how to make the two play nice (more as a re

August 11th, 2011

Book review: “EJB 3.1 Cookbook” by Richard M. Reese

Review

The kind people at Packt gave me a copy of “EJB 3.1 Cookbook” by Richard M. Reese to review.

There has been this idea running around the back of my head for a while, and it's only now that it is starting to crystalize into something that I can express.

When we look at Open Source projects, we see that there is a hierarchy of involvement.

3

In relation to http://dhanji.github.com/#unit-tests-false-idol here is the tail of the worst test case I ever came across...

In a former employers, there was an employee who we will call Kevin McCallister in order to protect the guilty.

1

This is a tail about Jenkins (née Hudson) and Kohsuke's policy of maintaining backwards compatibility...

Back in 2006 I started working for my previous employer, just a month or two after Peter Reilly started. Initially we were working on the same team.

2

In my previous post showed how easy it is to run your java application on CloudBees' RUN@cloud service. Today I'm going to use the CloudBees Deployer plugin for Jenkins that allows you to deploy your app to the cloud from your CI server.

I work for CloudBees Inc., they are a great company with great products.

1

The following quick and dirty bash script will take a pom and a jar and fake a maven build based on the source files for that that can be found in the current directory.

Really useful when running mvn dependency:analyze on a project you are validating POMs for.

OK, so a while back I posted my bash script for selecting the maven version to use for the current session http://s.apache.org/FQ2

Now that I have a Mac for my full time development machine, I thought I would share my version of these functions for Mac users:

usemvn () { if [ -z "$1" -o ! -x "/usr

2

#!/bin/bash

URL="$(svn info | sed -n -e '/^URL:/{s/URL: *//p}')"

ROOT="$(svn info | sed -n -e "/^Repository Root:/{s/Repository Root: *//p}")"

NEW_PATH="${URL#$ROOT}"

OLD_URL="$(sed -n '/< *scm *>/,/< *\/scm *>/p' pom.xml | sed -n '/< *connection *>/,/< *\/ *connection *>/{s/.*connection *> *scm

Here's a handy string for i18n testing:

用户汉语

It can be handy to have a string in a non-english charater set that can be pushed end-to-end.

An interesting blog on QA and development: The Clean Coder: QA or When do you flip a pancake?

I had my first oportunity to see the new 3D TV's this weekend.  They looked OK to me.  There was a 3D effect.  All around me were people who were going "wow! this is great! this is absolutely fantastic!".

Works on *nix

find ~/.m2/repository -type d -name \*-SNAPSHOT -exec rm -rvf {} \;

By searching for the directories we should catch the -YYYYMMDD.HHMMSS format of snapshots also

2

We recently lost our hudson server due to a multiple disk failure in the RAID array storing our hudson configuration. [5 of the 15 disks died]

So I've been looking into a backup script that will allow us to keep a backup of the configuration.

5

Cool post I just found:

http://coldattic.info/shvedsky/pro/blogs/a-foo-walks-into-a-bar/posts/7

When you are keeping build/version number in strings, you really need to left pad with 0's in order for string sorting to work...

e.g. 1.0-alpha-2 > 1.0-alpha-1 > 1.0-alpha-10

So as long as we start off with a leading zero, we are fine, e.g.

I am starting a series of things you should not do:

Seam has this handy annotation: @Synchronized which ensures that only a single thread may access the methods/fields of the component at the same time.

I've been meaning to blog about getting transaction management working with OpenEjb and Jetty using jetty:run... it's still an on-going story... but the following might get you going...

First off, in your pom.xml you need to add the configuration for maven-jetty-plugin...

10

Review Board is quite nice... it has a handy program for posting reviews (postreview)... and you can integrate this into your subversion hook scripts quite nicely...

But what if you want to automate submitting reviews on only parts of your code base...

2
About Me
About Me
Labels
Useful links
Blog Archive
Loading
Dynamic Views theme. Powered by Blogger. Report Abuse.