/**
* Copyright 2010 Mirko Friedenhagen
*/
package hudson.plugins.jobConfigHistory;
import hudson.Util;
import hudson.XmlFile;
import hudson.model.Hudson;
import hudson.model.User;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
/**
* Defines some helper functions needed by {@link JobConfigHistoryJobListener} and
* {@link JobConfigHistorySaveableListener}.
*
* @author mfriedenhagen
*/
public enum ConfigHistoryListenerHelper {
/**
* Helper for job creation.
*/
CREATED("Created"),
/**
* Helper for job rename.
*/
RENAMED("Renamed"),
/**
* Helper for job change.
*/
CHANGED("Changed"),
/**
* Helper for job deleted.
*/
DELETED("Deleted");
/**
* Name of the operation.
*/
private final String operation;
/**
*
* @param operation
* the operation we handle.
*/
ConfigHistoryListenerHelper(final String operation) {
this.operation = operation;
}
/**
* Returns the configuration history directory for the given {@link Item}.
*
* @param item
* for which we want to save the configuration.
* @return base directory where to store the history.
*/
//private File getConfigsDir(Item item) {
// return new File(item.getRootDir(), "config-history");
//}
/**
* Creates a timestamped directory to save the configuration beneath.
* Purges old data if configured
*
* @param xmlFile
* the current xmlFile configuration file to save
* @param timestamp
* time of operation.
* @return timestamped directory where to store one history entry.
*/
private File getRootDir(final XmlFile xmlFile, final Calendar timestamp) {
final JobConfigHistory plugin = Hudson.getInstance().getPlugin(JobConfigHistory.class);
final File itemHistoryDir = plugin.getHistoryDir(xmlFile);
// perform check for purge here, when we are actually going to create
// a new directory, rather than just when we scan it in above method.
plugin.checkForPurgeByQuantity(itemHistoryDir);
final File f = new File(itemHistoryDir, getIdFormatter().format(timestamp.getTime()));
// mkdirs sometimes fails although the directory exists afterwards,
// so check for existence as well and just be happy if it does.
if (!(f.mkdirs() || f.exists())) {
throw new RuntimeException("Could not create rootDir " + f);
}
return f;
}
/**
* Creates a new backup of the job configuration.
*
* @param xmlFile
* configuration file for the item we want to backup
*/
public final void createNewHistoryEntry(final XmlFile xmlFile) {
try {
final Calendar timestamp = new GregorianCalendar();
final File timestampedDir = getRootDir(xmlFile, timestamp);
if (this != DELETED) {
copyConfigFile(xmlFile.getFile(), timestampedDir);
}
createHistoryXmlFile(timestamp, timestampedDir);
} catch (IOException e) {
throw new RuntimeException("Operation " + operation + " on " + xmlFile + " did not succeed", e);
}
}
/**
* Creates the historical description for this action.
*
* @param timestamp
* when the action did happen.
* @param timestampedDir
* the directory where to save the history.
* @throws IOException
* if writing the history fails.
*/
private void createHistoryXmlFile(final Calendar timestamp, final File timestampedDir) throws IOException {
final User currentUser = getCurrentUser();
final String user;
final String userId;
if (currentUser != null) {
user = currentUser.getFullName();
userId = currentUser.getId();
} else {
user = "Anonym";
userId = "anonymous";
}
final XmlFile historyDescription = new XmlFile(new File(timestampedDir, JobConfigHistoryConsts.HISTORY_FILE));
final HistoryDescr myDescr = new HistoryDescr(user, userId, operation, getIdFormatter().format(
timestamp.getTime()));
historyDescription.write(myDescr);
}
/**
* Returns the user who invoked the action.
*
* @return current user.
*/
User getCurrentUser() {
return User.current();
}
/**
* Saves a copy of this project's {@code config.xml} into {@code timestampedDir}.
*
* @param currentConfig
* which we want to copy.
* @param timestampedDir
* the directory where to save the copy.
* @throws FileNotFoundException
* if initiating the file holding the copy fails.
* @throws IOException
* if writing the file holding the copy fails.
*/
private void copyConfigFile(final File currentConfig, final File timestampedDir) throws FileNotFoundException, IOException {
final FileOutputStream configCopy = new FileOutputStream(new File(timestampedDir, currentConfig.getName()));
try {
final FileInputStream configOriginal = new FileInputStream(currentConfig);
try {
Util.copyStream(configOriginal, configCopy);
} finally {
configOriginal.close();
}
} finally {
configCopy.close();
}
}
/**
* Returns a simple formatter used for creating timestamped directories. We create this every time as
* {@link SimpleDateFormat} is <b>not</b> threadsafe.
*
* @return the idFormatter
*/
SimpleDateFormat getIdFormatter() {
return new SimpleDateFormat(JobConfigHistoryConsts.ID_FORMATTER);
}
}