package hudson.plugins.lockedfilesreport;
import hudson.tasks.BuildStepMonitor;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.List;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.FilePath.FileCallable;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.StreamBuildListener;
import hudson.model.TopLevelItem;
import hudson.plugins.lockedfilesreport.model.FileUsageDetails;
import hudson.remoting.VirtualChannel;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;
import hudson.util.FormValidation;
public class LockedFilesReporter extends Recorder implements Serializable {
private static final long serialVersionUID = 1L;
@DataBoundConstructor
public LockedFilesReporter() {
super();
}
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
return checkForFileUsage(build, listener, launcher);
}
@Override
public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) {
return checkForFileUsage(build, listener, build.getBuiltOn().createLauncher(listener));
}
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.BUILD;
}
@Override
public LockedFilesReporter.DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}
private boolean checkForFileUsage(AbstractBuild<?, ?> build, BuildListener listener, Launcher launcher) {
if (((build.getResult() != null) && build.getResult().isWorseOrEqualTo(Result.FAILURE)) || (build.getAction(LockedFilesReportAction.class) != null)) {
return true;
}
FindFilesInUseCommand command;
if (launcher.isUnix()) {
command = new FindFilesInUseWithLsof();
} else {
command = new FindFilesInUseWithHandle(getDescriptor().getHandleExecutable());
}
try {
listener.getLogger().println("Searching for locked files in workspace.");
FilePath workspace = build.getBuiltOn().getWorkspaceFor((TopLevelItem) build.getProject());
List<FileUsageDetails> list = workspace.act(new GetUsedFiles(command, new StreamBuildListener(listener.getLogger())));
if (list.size() > 0) {
build.getActions().add(new LockedFilesReportAction(build, list));
listener.error("Build was failed as the workspace contained files that were locked by another process. See Locked files report for more information.");
build.setResult(Result.FAILURE);
}
return (list.size() == 0);
} catch (IOException e) {
listener.error("There was an IOException while launching a process. Please report it to the Hudson user mailing list.");
e.printStackTrace(listener.getLogger());
} catch (InterruptedException e) {
listener.error("There was an InterruptedException while running a process. Please report it to the Hudson user mailing list.");
e.printStackTrace(listener.getLogger());
}
return false;
}
static class GetUsedFiles implements FileCallable<List<FileUsageDetails>> {
private static final long serialVersionUID = 1L;
private final FindFilesInUseCommand command;
private final StreamBuildListener listener;
public GetUsedFiles(FindFilesInUseCommand command, StreamBuildListener listener) {
this.command = command;
this.listener = listener;
}
public List<FileUsageDetails> invoke(File f, VirtualChannel channel) throws IOException {
String workspacePath = f.getCanonicalPath();
ByteArrayOutputStream commandOutput = new ByteArrayOutputStream();
BufferedReader reader = null;
try {
int result = new Launcher.LocalLauncher(listener).launch().cmds(command.getArguments(workspacePath)).stdout(commandOutput).start().join();
reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(commandOutput.toByteArray())));
return command.parseOutput(result, reader, workspacePath);
} catch (InterruptedException e) {
throw new IOException(e);
} finally {
IOUtils.closeQuietly(commandOutput);
if (reader != null) {
IOUtils.closeQuietly(reader);
}
}
}
}
@Extension
public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {
private String handleExecutable;
public DescriptorImpl() {
super(LockedFilesReporter.class);
handleExecutable = "handle.exe";
load();
}
@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
req.bindJSON(this, json);
save();
return true;
}
@Override
public String getDisplayName() {
return "Locked files report";
}
public FormValidation doCheckHandleExecutable(@QueryParameter String value) {
return FormValidation.validateExecutable(value);
}
public String getHandleExecutable() {
return handleExecutable;
}
public void setHandleExecutable(String handleExecutable) {
this.handleExecutable = handleExecutable;
}
}
}