NAT Tree List with GlazedLists and NatTable
This is my exercise code to create a Tree List with expand/collapsible subgroups.
The code was derived from Vogellas Nebula NatTable – Commands & Events – Tutorial.
My Code
The data rows of the table:
package tryout.treelist;
public class Area {
private String name;
private long population;
public Area(String name, long population) {
super();
this.name = name;
this.population = population;
}
public String getName() {
return name;
}
public long getPopulation() {
return population;
}
@Override
public String toString() {
return name + " Population: " + population;
}
}
The tree node wrapper
package tryout.treelist;
import java.util.ArrayList;
import java.util.List;
public class TreeItem<T> {
private T item;
private TreeItem<T> parent;
private List<TreeItem<T>> children = new ArrayList<>();
public TreeItem(T item, TreeItem<T> parent){
this.item=item;
this.parent=parent;
this.parent.addChild(this);
}
public TreeItem(T content) {
this.item=content;
}
private void addChild(TreeItem<T> treeItem) {
children.add(treeItem);
}
public TreeItem<T> getParent() {
return parent;
}
public boolean hasChildren() {
return children.size() > 0;
}
public T getItem() {
return item;
}
}
The tree format definition for such nodes:
package tryout.treelist;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import ca.odell.glazedlists.TreeList;
class TreeItemFormat implements TreeList.Format<TreeItem<Area>> {
@Override
public void getPath(List<TreeItem<Area>> path, TreeItem<Area> element) {
List<TreeItem<Area>> path1 = new ArrayList<>();
if (element.getParent() != null) {
getPath(path1, element.getParent());
path.addAll(path1);
}
path.add(element);
}
@Override
public boolean allowsChildren(TreeItem<Area> element) {
return element.hasChildren();
}
@Override
public Comparator<? super TreeItem<Area>> getComparator(int depth) {
return (o1, o2) -> o1.getItem().toString().compareTo(o2.getItem().toString());
}
}
The GUI view defining the NAT table and the data fed to it:
package tryout.treelist;
import java.util.Collection;
import javax.annotation.PostConstruct;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.data.ExtendedReflectiveColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.tree.GlazedListTreeData;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.tree.GlazedListTreeRowModel;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import com.google.common.collect.ImmutableList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.TreeList;
public class SimpleTreePart {
@PostConstruct
public void createComposite(Composite parent) {
parent.setLayout(new GridLayout());
// Properties of the Area items inside the TreeItems
String[] propertyNames = { "item.name", "item.population" };
IColumnPropertyAccessor<TreeItem<Area>> columnPropertyAccessor = new ExtendedReflectiveColumnPropertyAccessor<TreeItem<Area>>(
propertyNames);
EventList<TreeItem<Area>> eventList = GlazedLists.eventList(getSampleAreaTreeItems());
TreeList<TreeItem<Area>> treeList = new TreeList<TreeItem<Area>>(eventList, new TreeItemFormat(),
TreeList.nodesStartExpanded());
ListDataProvider<TreeItem<Area>> dataProvider = new ListDataProvider<>(treeList, columnPropertyAccessor);
DataLayer dataLayer = new DataLayer(dataProvider);
setColumWidthPercentage(dataLayer);
GlazedListTreeData<TreeItem<Area>> glazedListTreeData = new GlazedListTreeData<>(treeList);
GlazedListTreeRowModel<TreeItem<Area>> glazedListTreeRowModel = new GlazedListTreeRowModel<>(
glazedListTreeData);
TreeLayer treeLayer = new TreeLayer(dataLayer, glazedListTreeRowModel);
treeLayer.setRegionName(GridRegion.BODY);
new NatTable(parent, treeLayer, true);
GridLayoutFactory.fillDefaults().generateLayout(parent);
}
private Collection<TreeItem<Area>> getSampleAreaTreeItems() {
TreeItem<Area> globe = new TreeItem<Area>(new Area("Globe", 8000));
TreeItem<Area> europe = new TreeItem<Area>(new Area("Europe", 983), globe);
TreeItem<Area> switzerland = new TreeItem<Area>(new Area("Switzerland", 8), europe);
TreeItem<Area> germany = new TreeItem<Area>(new Area("Germany", 85), europe);
TreeItem<Area> russia = new TreeItem<Area>(new Area("Russia", 140), europe);
TreeItem<Area> asia = new TreeItem<Area>(new Area("Asia", 3203), globe);
TreeItem<Area> japan = new TreeItem<Area>(new Area("Japan", 203), asia);
TreeItem<Area> china = new TreeItem<Area>(new Area("China", 1230), asia);
TreeItem<Area> india = new TreeItem<Area>(new Area("India", 1130), asia);
return ImmutableList.of(globe, asia, japan, china, india, europe, switzerland, germany, russia);
}
private void setColumWidthPercentage(DataLayer dataLayer) {
dataLayer.setColumnPercentageSizing(true);
dataLayer.setColumnWidthPercentageByPosition(0, 50);
dataLayer.setColumnWidthPercentageByPosition(1, 50);
}
}
And the main class to run the code:
package tryout.treelist;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class TreeListMain {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 130);
shell.setText("Tree List Example");
shell.setLayout(new RowLayout());
new SimpleTreePart().createComposite(shell);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
Jenkins Trigger Child Job
Notizen zu meiner Odysse zum Versuch, aus einem Jenkins Job einen Folge-Job mit jeweils unterschiedlichen Parametern aufzurufen.
Wie es funktioniert
Variante 1 (getestet)
Das Parameterized Trigger Plugin, wenn es in der Build (nicht Post-Build) Phase configuriert wird so aufgerufen werden, dass für jedes Configurations-File (welches auf das definierte File-Name-Pattern passt) einmal der definierte Job aufgerufen wird. (–> Hier der Hinweis)
Wenn das Parameterized Trigger Plugin jedoch in der Post-Build phase configuriert wird ist das Trigger-Pro-File Feature leider nicht konfigurierbar.
Variante 2 (nicht getestet)
Aufruf über HTTP. Diese Variante hab ich nicht ausgetestet, weil ich befürchtet habe in Jenkins meines Arbeitgebers nicht so leicht an die Authorisierungs-Tokens zu gelangen und eine so geartete Lösung produktiv so liefern zu können.
Links:
- Jenkins Remote Build Trigger – Build Jobs with URL and Token
- Trigger Jenkins Job Remotely using Jenkins API
Was ich auch noch gelernt habe
Aufruf von Folge-Jobs via Groovy
Leider habe ich es nicht geschaft dem Groovy-mässigen Aufruf auch Parameter mit zu geben.
Der folgende Code, eingefügt in in Groovy Plugin in Post-Build ruft tatsächlich den Folge-Job auf (aber leider one Parameter):
import jenkins.*
import jenkins.model.*
import hudson.*
import hudson.model.*
def build = Thread.currentThread().executable
def buildNumber = build.number
def target = Jenkins.instance.getItemByFullName("Mein_Jenkins_Job") ?: null
if (!target) {
manager.listener.logger.println("No downstream job found")
} else {
manager.listener.logger.println("I found it!")
def execution = target.scheduleBuild2(0, new Cause.UpstreamCause(build))
manager.listener.logger.println("Running test job:")
manager.listener.logger.println("Complete, result was: " + execution.get().result)
}
Dokumentation hudson.model.AbstractProject Klasse, dessen scheduleBuild2() Methode aufgerufen wird.
Die doBuildWithParameters Methode ist leider deprecated und für den Ersatz sind keine Beispiele auffindbar.
–> Inspiration für diesen Code
–> Groovy Syntax
Definition Folge-Job als Multi-Configuration-Projekt & Aufruf einer spezifischen Ausprägung davon
Multikonfigurations-Jobs haben typischerweise eine Dimension in der sie sich unterscheiden, z.B. die Dimension Betriebssystem (Windows, Linux, GoogleOS, …). Für jede dieser Dimensionen wird eine Instanz generiert. Wenn man den Job startet, wird per Default für jede der Ausprägungen eine Instanz gestartet.
Nun sah es vielsversprechend aus, dass man einen solchen Job unter Mitgabe einer Dimensions-Ausprägung hätte staten können. Ich war bislang nicht er folgreich.
Readings: