Skip to content

Commit

Permalink
Stop holding a list of implementors in the template (closes #21)
Browse files Browse the repository at this point in the history
Support implementations in folders (closes #15)
  • Loading branch information
Marc Carter committed Aug 31, 2014
1 parent df9bc67 commit 3407011
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 179 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.export.Exported;

import java.io.IOException;
import java.util.logging.Logger;

/**
* User: Joel Johnson
* Date: 2/25/13
* Time: 10:11 PM
*/
public class TemplateImplementationProperty extends JobProperty<AbstractProject<?, ?>> {
private static final Logger LOG = Logger.getLogger("ez-templates");

Expand Down Expand Up @@ -66,37 +60,15 @@ public boolean getSyncDisabled() {
return syncDisabled;
}

public AbstractProject findProject() {
public AbstractProject findTemplate() {
return ProjectUtils.findProject(getTemplateJobName());
}

@Extension
public static class DescriptorImpl extends JobPropertyDescriptor {
@Override
public JobProperty<?> newInstance(StaplerRequest request, JSONObject formData) throws FormException {
AbstractProject implementationProject = ProjectUtils.findProject(request);

TemplateImplementationProperty oldProperty = (TemplateImplementationProperty) implementationProject.getProperty(TemplateImplementationProperty.class);

boolean hasNewProperty = formData.size() > 0 && formData.has("useTemplate");
boolean hasOldProperty = oldProperty != null;

if (hasOldProperty) {
boolean removeOldProperty = true;
AbstractProject oldTemplate = oldProperty.findProject();
if (hasNewProperty) {
String templateJobName = formData.getJSONObject("useTemplate").getString("templateJobName");
boolean namesMatch = oldTemplate != null && StringUtils.equals(templateJobName, oldTemplate.getFullName());
if (namesMatch) {
removeOldProperty = false;
}
}
if (removeOldProperty) {
removeImplementationFromTemplate(oldTemplate, implementationProject);
}
}

if (hasNewProperty) {
if (formData.size() > 0 && formData.has("useTemplate")) {
JSONObject useTemplate = formData.getJSONObject("useTemplate");

String templateJobName = useTemplate.getString("templateJobName");
Expand All @@ -105,8 +77,6 @@ public JobProperty<?> newInstance(StaplerRequest request, JSONObject formData) t
boolean syncBuildTriggers = useTemplate.getBoolean("syncBuildTriggers");
boolean syncDisabled = useTemplate.getBoolean("syncDisabled");

assignImplementationToTemplate(ProjectUtils.findProject(templateJobName), implementationProject);

return new TemplateImplementationProperty(templateJobName, syncMatrixAxis, syncDescription, syncBuildTriggers, syncDisabled);
}

Expand All @@ -130,44 +100,6 @@ public ListBoxModel doFillTemplateJobNameItems() {
return items;
}

private static void removeImplementationFromTemplate(AbstractProject templateProject, AbstractProject implementationProject) throws FormException {
if (templateProject == null) {
// Could just be an empty name!
LOG.warning(String.format("Cannot remove [%s] from missing template", implementationProject.getFullDisplayName()));
return;
}
@SuppressWarnings("unchecked")
TemplateProperty property = (TemplateProperty) templateProject.getProperty(TemplateProperty.class);

if (property != null && property.removeImplementation(implementationProject.getName())) {
LOG.info(String.format("Removing [%s] from template [%s]", implementationProject.getFullDisplayName(), templateProject.getFullDisplayName()));
saveTemplate(templateProject);
}
}

private static void assignImplementationToTemplate(AbstractProject templateProject, AbstractProject implementationProject) throws FormException {
if (templateProject == null) {
// May not have configured it yet
return;
}
@SuppressWarnings("unchecked")
TemplateProperty property = (TemplateProperty) templateProject.getProperty(TemplateProperty.class);

if (property != null && property.addImplementation(implementationProject.getName())) {
LOG.info(String.format("Assigning [%s] to template [%s]", implementationProject.getFullDisplayName(), templateProject.getFullDisplayName()));
// We did add a new implementation to the template (if it already used that project addImplementation returns false)
saveTemplate(templateProject);
}
}

private static void saveTemplate(AbstractProject templateProject) throws FormException {
try {
ProjectUtils.silentSave(templateProject);
} catch (IOException e) {
throw new FormException(e, "templateJobName");
}
}

@Override
public String getDisplayName() {
return Messages.TemplateImplementationProperty_displayName();
Expand Down
56 changes: 15 additions & 41 deletions src/main/java/com/joelj/jenkins/eztemplates/TemplateProperty.java
Original file line number Diff line number Diff line change
@@ -1,71 +1,45 @@
package com.joelj.jenkins.eztemplates;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.joelj.jenkins.eztemplates.utils.ProjectUtils;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.model.JobProperty;
import hudson.model.JobPropertyDescriptor;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Collection;

/**
* User: Joel Johnson
* Date: 2/25/13
* Time: 10:11 PM
*/
public class TemplateProperty extends JobProperty<AbstractProject<?, ?>> {
private final Set<String> implementations;

@DataBoundConstructor
public TemplateProperty(Set<String> implementations) {
this.implementations = implementations;
}

public Set<String> getImplementations() {
return implementations;
}
public static Collection<AbstractProject> getImplementations(final String templateFullName) {
Collection<AbstractProject> projects = ProjectUtils.findProjectsWithProperty(TemplateImplementationProperty.class);
return Collections2.filter(projects, new Predicate<AbstractProject>() {
@Override
public boolean apply(AbstractProject abstractProject) {
TemplateImplementationProperty prop = (TemplateImplementationProperty) abstractProject.getProperty(TemplateImplementationProperty.class);
return templateFullName.equals(prop.getTemplateJobName());
}
});

public boolean addImplementation(String projectName) {
return implementations.add(projectName);
}

public boolean removeImplementation(String projectName) {
return implementations.remove(projectName);
public Collection<AbstractProject> getImplementations() {
return getImplementations(owner.getFullName());
}

@Extension
public static class DescriptorImpl extends JobPropertyDescriptor {
@Override
public JobProperty<?> newInstance(StaplerRequest request, JSONObject formData) throws FormException {
if (formData.size() > 0) {
Set<String> implementationJobs = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());

AbstractProject thisProject = ProjectUtils.findProject(request);
if (thisProject != null) {
@SuppressWarnings("unchecked")
TemplateProperty property = (TemplateProperty) thisProject.getProperty(TemplateProperty.class);

if (property != null) {
Set<String> oldImplementationList = property.getImplementations();
implementationJobs.addAll(oldImplementationList);
}
}

return new TemplateProperty(implementationJobs);
return new TemplateProperty();
}
return null;
}

@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
return super.configure(req, json);
}

@Override
public String getDisplayName() {
return Messages.TemplateImplementationProperty_displayName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@
import java.util.Collection;
import java.util.List;

/**
* User: Joel Johnson
* Date: 2/25/13
* Time: 11:49 PM
*/
public class ProjectUtils {

public static Collection<AbstractProject> findProjectsWithProperty(final Class<? extends JobProperty<?>> property) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* User: Joel Johnson
* Date: 2/26/13
* Time: 5:42 PM
*/
public class ReflectionUtils {
public static <T> T getFieldValue(Class c, Object instance, String name) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,84 +16,48 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
* This is where all the magic really happens.
* The templates and implementations, when they change, call one of the two public handle* methods.
* <p/>
* User: Joel Johnson
* Date: 2/25/13
* Time: 10:55 PM
*/
public class TemplateUtils {
private static final Logger LOG = Logger.getLogger("ez-templates");

public static void handleTemplateSaved(AbstractProject templateProject, TemplateProperty property) throws IOException {
LOG.info(String.format("Template [%s] was saved. Syncing implementations.", templateProject.getFullDisplayName()));
Set<String> implementations = property.getImplementations();

Iterator<String> iterator = implementations.iterator();
boolean changedTemplateProject = false;
while (iterator.hasNext()) {
String implementationName = iterator.next();
AbstractProject project = ProjectUtils.findProject(implementationName);
if (project == null) {
LOG.warning(String.format("[%s] doesn't exist as a project. Cleaning it out of the template.", implementationName));
changedTemplateProject = true;
iterator.remove();
continue;
}

@SuppressWarnings("unchecked")
TemplateImplementationProperty impProperty = (TemplateImplementationProperty) project.getProperty(TemplateImplementationProperty.class);

if (impProperty == null || !templateProject.getFullName().equals(impProperty.getTemplateJobName())) {
LOG.warning(String.format("[%s] doesn't inherit from this template. Cleaning it out of the template.", implementationName));
changedTemplateProject = true;
iterator.remove();
continue;
}

handleTemplateImplementationSaved(project, impProperty);
}

if (changedTemplateProject) {
ProjectUtils.silentSave(templateProject);
for (AbstractProject impl : property.getImplementations()) {
TemplateImplementationProperty implProperty = (TemplateImplementationProperty) impl.getProperty(TemplateImplementationProperty.class);
handleTemplateImplementationSaved(impl, implProperty);
}
}

public static void handleTemplateDeleted(AbstractProject templateProject, TemplateProperty property) throws IOException {
LOG.info(String.format("Template [%s] was deleted. Removing from implementations.", templateProject.getFullDisplayName()));
for (String implementationName : property.getImplementations()) {
AbstractProject implementationProject = ProjectUtils.findProject(implementationName);
if (implementationProject != null) {
LOG.info(String.format("Removing template from [%s].", implementationProject.getFullDisplayName()));
implementationProject.removeProperty(TemplateImplementationProperty.class);
ProjectUtils.silentSave(implementationProject);
}
LOG.info(String.format("Template [%s] was deleted.", templateProject.getFullDisplayName()));
for (AbstractProject impl : property.getImplementations()) {
LOG.info(String.format("Removing template from [%s].", impl.getFullDisplayName()));
TemplateImplementationProperty implProperty = (TemplateImplementationProperty) impl.getProperty(TemplateImplementationProperty.class);
impl.removeProperty(TemplateImplementationProperty.class);
ProjectUtils.silentSave(impl);
}
}

public static void handleTemplateRename(AbstractProject templateProject, TemplateProperty property, String oldName, String newName) throws IOException {
public static void handleTemplateRename(AbstractProject templateProject, TemplateProperty property, String oldFullName, String newFullName) throws IOException {
LOG.info(String.format("Template [%s] was renamed. Updating implementations.", templateProject.getFullDisplayName()));
for (String implementationName : property.getImplementations()) {
AbstractProject implementationProject = ProjectUtils.findProject(implementationName);
if (implementationProject != null) {
LOG.info(String.format("Updating template in [%s].", implementationProject.getFullDisplayName()));
TemplateImplementationProperty implementationProperty = (TemplateImplementationProperty) implementationProject.getProperty(TemplateImplementationProperty.class);
if (oldName.equals(implementationProperty.getTemplateJobName())) {
implementationProperty.setTemplateJobName(newName);
ProjectUtils.silentSave(implementationProject);
}
for (AbstractProject impl : TemplateProperty.getImplementations(oldFullName)) {
LOG.info(String.format("Updating template in [%s].", impl.getFullDisplayName()));
TemplateImplementationProperty implProperty = (TemplateImplementationProperty) impl.getProperty(TemplateImplementationProperty.class);
if (oldFullName.equals(implProperty.getTemplateJobName())) {
implProperty.setTemplateJobName(newFullName);
ProjectUtils.silentSave(impl);
}
}
}

public static void handleTemplateImplementationSaved(AbstractProject implementationProject, TemplateImplementationProperty property) throws IOException {
LOG.info(String.format("Implementation [%s] was saved. Syncing with [%s].", implementationProject.getFullDisplayName(), property.getTemplateJobName()));
AbstractProject templateProject = property.findProject();
AbstractProject templateProject = property.findTemplate();
if (templateProject == null) {
throw new IllegalStateException(String.format("Cannot find template [%s] used by job [%s]", property.getTemplateJobName(), implementationProject.getFullDisplayName()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ul>
<j:forEach var="implementor" items="${instance.implementations}">
<li>
<a href="${rootURL}/job/${implementor}">${implementor}</a>
<a href="${rootURL}/${implementor.url}">${implementor.fullDisplayName}</a>
</li>
</j:forEach>
</ul>
Expand Down

0 comments on commit 3407011

Please sign in to comment.