I am creating a plug-in for Jenkins. To creates plug-ins for Jenkins, you have to use this weird scripting language called: Jelly, which sets up the UI in html and such, and uses Java to manage the UI components. In Jelly, you can set up fields that repeat themselves (for example, if you wanted to provide several different properties or something).
For what I'm doing, I have repeatable fields for "Component" and each Component has repeatable fields for "Property".
This is a jelly file for an individual Component:
<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">
	    <f:entry title="Component" field="component">
	    	<f:select />
	  </f:entry>
	  <f:entry field="updatePropertyParams">
	  	<f:repeatableProperty field="updatePropertyParams" header="Property" minimum="1"/>
	  </f:entry>
	<div align="right">
		<f:repeatableDeleteButton/>
	</div>
</j:jelly>
And this is a jelly file for an individual Property:
<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">
	<f:entry title="Property to Update" field="updateProperty">
		<f:select />
	</f:entry>
	<f:entry title="Value" field="updatePropertyValue">
		<f:textbox />
	</f:entry>
	<div align="right">
		<f:repeatableDeleteButton/>
	</div>
</j:jelly>
At runtime, the values are loaded into a class called: ComponentParam:
import hudson.Extension;
import hudson.RelativePath;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.util.ListBoxModel;
import java.util.List;
import jfullam.vfabric.jenkins.plugin.rest.ServiceManager;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
 
public class ComponentParam extends AbstractDescribableImpl<ComponentParam> {
    @Extension
    public static class DescriptorImpl extends Descriptor<ComponentParam> {
        /**
         * Get the list of components for a specific deployment.
         *
         * @param deployment - a deployment id
         * @return ListBoxModel - list of components
         */
        public ListBoxModel doFillComponentItems(@QueryParameter("deploymentNode") @RelativePath("..") String deploymentNode) {
            return ServiceManager.userInterfaceService().getComponents(deploymentNode);
        }
        /**
         * {@inheritDoc} Required implementation.
         */
        @Override
        public String getDisplayName() {
            return "Update Component";
        }
    }
 
    private String component;
    private List<UpdatePropertyParam> updatePropertyParams;
 
    @DataBoundConstructor
    public ComponentParam(String component, List<UpdatePropertyParam> updatePropertyParams) {
        this.component = component;
        setUpdatePropertyParams(updatePropertyParams);
    }
... // Here follows just a bunch of setters and getters
}
Notice how the parameters in the class directly match up with the field names in the Component jelly file. These values appear to be automatically loaded into the class with the DataBoundConstructor annotation.
The list of Properties for a given Component is then automatically resolved with the nested class's "doFillComponentItems" method. Notice also how the nested class has the annotation: Extension.
The class for updating the Component's properties follows the same procedure:
import hudson.Extension;
import hudson.RelativePath;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.util.ListBoxModel;
import jfullam.vfabric.jenkins.plugin.rest.ServiceManager;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
 
/**
 * @author am185255
 */
public class UpdatePropertyParam extends AbstractDescribableImpl<UpdatePropertyParam> {
 
    @Extension
    public class DescriptorImpl extends Descriptor<UpdatePropertyParam> {
        /**
         * Gets a list of update properties for a specific component.
         *
         * @param component
         * @return
         * @return ListBoxModel - the list of update properties for the component
         */
        public ListBoxModel doFillUpdatePropertyItems(@QueryParameter("component") @RelativePath("..") String component) {
            // public ListBoxModel doFillUpdatePropertyItems(String component) {
            /*for(UpdatePropertyParam param : getUpdatePropertyParams()) {
                ((UpdatePropertyParam.DescriptorImpl)param.getDescriptor()).doFillUpdatePropertyItems(component);
            }
             */
            log.info("Update Property Nested: " + component);
            // return ServiceManager.userInterfaceService().getUpdateProperties(component);
            ListBoxModel ret = ServiceManager.userInterfaceService().getUpdateProperties(component);
            if (getParent() != null) {
                for (UpdatePropertyParam otherParam : getParent().getUpdatePropertyParams()) {
                    for (int i = 0; i < ret.size(); i++) {
                        log.info("Option: " + ret.get(i).name + "," + ret.get(i).value);
                        if (ret.get(i).name.equals(otherParam.getUpdateProperty())) {
                            ret.remove(i);
                            i--;
                        }
                    }
                }
            } else {
                log.info("Null Parent.");
            }
 
            return ret;
        }
 
        /**
         * {@inheritDoc} Required implementation.
         */
        @Override
        public String getDisplayName() {
            return "Update property";
        }
 
    }
 
    private static final Log log = LogFactory.getLog(UpdatePropertyParam.class);
 
    private ComponentParam parent;
 
    private String updateProperty;
 
    private String updatePropertyValue;
 
    @DataBoundConstructor
    public UpdatePropertyParam(String updateProperty, String updatePropertyValue) {
        this.updateProperty = updateProperty;
        this.updatePropertyValue = updatePropertyValue;
    }
 
    /**
     * Retrieves the value for {@link #parent}.
     *
     * @return the current value
     */
    public ComponentParam getParent() {
        return parent;
    }
... // Here follows a bunch of setters and getters
}
As with the Component one, the field variables match the same names as in the jelly files (with the exception of "parent", which I will mention later).

Okay, so I was tasked with the job of seeing if I could remove duplicates from the drop down lists. You can modify multiple properties for a given Component. When you add another field for modifying the properties at runtime, the list of options include all of the possible properties, including properties which have already been included in the other instances.
So if the Component was structured like this:
Component
--Property1
--Property2
--Property3
If the first modifying field was set to:
Component
--Property2
The second modifying field (and all after that) would only allow you to select:
Component
--Property1
--Property3

Now, the problem I am having is trying to make the data visible between the different instances of the UpdatePropertyParam. This is a problem because the list of options is resolved in the nested class.
I attempted to resolve this by setting up a callback to the ComponentParam object (ie: the "parent" variable), which meant that I had to make the nested class non-static. However, when I did that and tried to build the project, I got the error:
UpdatePropertyParam.java:[25,5] annotated nested classes must be static
So that plan was shot down.

Given that the nested class MUST have that annotation (as far as I know), and the updating of the drop-down list options MUST happen in that nested class with that doFillUpdatePropertyItems() method, does anyone know how I can expose all the instances of the outer class so I can remove duplicate values?