/*******************************************************************************
*
* Copyright (c) 2004-2009 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* Kohsuke Kawaguchi, Martin Eigenbrodt, Peter Hayes
*
*
*******************************************************************************/
package hudson.tasks;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.Extension;
import hudson.EnvVars;
import hudson.model.*;
import hudson.util.FormValidation;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.AncestorInPath;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
/**
* Saves Javadoc for the project and publish them.
*
* @author Kohsuke Kawaguchi
*/
public class JavadocArchiver extends Recorder {
/**
* Path to the Javadoc directory in the workspace.
*/
private final String javadocDir;
/**
* If true, retain javadoc for all the successful builds.
*/
private final boolean keepAll;
@DataBoundConstructor
public JavadocArchiver(String javadoc_dir, boolean keep_all) {
this.javadocDir = javadoc_dir;
this.keepAll = keep_all;
}
/**
* @inheritDoc
*/
@Override
public boolean needsToRun(Result buildResult) {
//TODO it seems we shouldn't archive javadocs if build result is worse than SUCCESS, investigate this
return buildResult.isBetterThan(Result.ABORTED);
}
public String getJavadocDir() {
return javadocDir;
}
public boolean isKeepAll() {
return keepAll;
}
/**
* Gets the directory where the Javadoc is stored for the given project.
*/
private static File getJavadocDir(AbstractItem project) {
return new File(project.getRootDir(), "javadoc");
}
/**
* Gets the directory where the Javadoc is stored for the given build.
*/
private static File getJavadocDir(Run run) {
return new File(run.getRootDir(), "javadoc");
}
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
listener.getLogger().println(Messages.JavadocArchiver_Publishing());
EnvVars env = build.getEnvironment(listener);
FilePath javadoc = build.getWorkspace().child(env.expand(javadocDir));
FilePath target = new FilePath(keepAll ? getJavadocDir(build) : getJavadocDir(build.getProject()));
try {
if (javadoc.copyRecursiveTo("**/*", target) == 0) {
if (build.getResult().isBetterOrEqualTo(Result.UNSTABLE)) {
// If the build failed, don't complain that there was no javadoc.
// The build probably didn't even get to the point where it produces javadoc.
listener.error(Messages.JavadocArchiver_NoMatchFound(javadoc, javadoc.validateAntFileMask("**/*")));
}
build.setResult(Result.FAILURE);
return true;
}
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError(Messages.JavadocArchiver_UnableToCopy(javadoc, target)));
build.setResult(Result.FAILURE);
return true;
}
// add build action, if javadoc is recorded for each build
if (keepAll) {
build.addAction(new JavadocBuildAction(build));
}
return true;
}
@Override
public Collection<Action> getProjectActions(AbstractProject project) {
return Collections.<Action>singleton(new JavadocAction(project));
}
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.NONE;
}
protected static abstract class BaseJavadocAction implements Action {
public String getUrlName() {
return "javadoc";
}
public String getDisplayName() {
if (new File(dir(), "help-doc.html").exists()) {
return Messages.JavadocArchiver_DisplayName_Javadoc();
} else {
return Messages.JavadocArchiver_DisplayName_Generic();
}
}
public String getIconFileName() {
if (dir().exists()) {
return "help.png";
} else // hide it since we don't have javadoc yet.
{
return null;
}
}
/**
* Serves javadoc.
*/
public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
new DirectoryBrowserSupport(this, new FilePath(dir()), getTitle(), "help.png", false).generateResponse(req, rsp, this);
}
protected abstract String getTitle();
protected abstract File dir();
}
public static class JavadocAction extends BaseJavadocAction implements ProminentProjectAction {
private final AbstractItem project;
public JavadocAction(AbstractItem project) {
this.project = project;
}
protected File dir() {
// Would like to change AbstractItem to AbstractProject, but is
// that a backwards compatible change?
if (project instanceof AbstractProject) {
AbstractProject abstractProject = (AbstractProject) project;
Run run = abstractProject.getLastSuccessfulBuild();
if (run != null) {
File javadocDir = getJavadocDir(run);
if (javadocDir.exists()) {
return javadocDir;
}
}
}
return getJavadocDir(project);
}
protected String getTitle() {
return project.getDisplayName() + " javadoc";
}
}
public static class JavadocBuildAction extends BaseJavadocAction {
private final AbstractBuild<?, ?> build;
public JavadocBuildAction(AbstractBuild<?, ?> build) {
this.build = build;
}
protected String getTitle() {
return build.getDisplayName() + " javadoc";
}
protected File dir() {
return getJavadocDir(build);
}
}
@Extension
public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {
public String getDisplayName() {
return Messages.JavadocArchiver_DisplayName();
}
/**
* Performs on-the-fly validation on the file mask wildcard.
*/
public FormValidation doCheck(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException, ServletException {
FilePath ws = project.getSomeWorkspace();
return ws != null ? ws.validateRelativeDirectory(value) : FormValidation.ok();
}
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}
}
}