tag:blogger.com,1999:blog-34601027931861408982024-03-21T08:25:53.543+01:00Code & MeThis Blog deals with software tools, code snippets, and scripts I am working on. Focus is on Eclipse development and linux.Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.comBlogger118125tag:blogger.com,1999:blog-3460102793186140898.post-57822755513408857992020-12-02T09:51:00.004+01:002020-12-02T09:52:50.970+01:00Add Checkstyle support to Eclipse, Maven, and Jenkins<p>After <a href="http://codeandme.blogspot.com/2020/11/add-pmd-support-to-eclipse-and-maven.html" target="_blank">PMD</a> and <a href="http://codeandme.blogspot.com/2020/11/add-spotbugs-support-to-eclipse-maven.html" target="_blank">SpotBugs</a> we will have a look at <a href="https://checkstyle.org/" target="_blank">Checkstyle</a> integration into the IDE and our maven builds. Parts of this tutorial are already covered by Lars' tutorial on <a href="https://www.vogella.com/tutorials/Checkstyle/article.html" target="_blank">Using the Checkstyle Eclipse plug-in</a>.</p><p><b><u>Step 1: Add Eclipse IDE Support</u></b></p><p>First install the <a href="https://checkstyle.org/eclipse-cs/#!/" target="_blank">Checkstyle Plugin</a> via the <a href="https://marketplace.eclipse.org/content/checkstyle-plug" target="_blank">Eclipse Marketplace.</a> Before we enable the checker, we need to define a ruleset to run against. As in the previous tutorials, we will setup project specific rules backed by one ruleset that can also be used by maven later on.</p><p>Create a new file for your rules in <i><yourProject>.releng/checkstyle/checkstyle_rules.xml</i>. If you are familiar with <a href="https://checkstyle.sourceforge.io/config.html" target="_blank">writing rules</a> just add them. In case you are new, you might want to <a href="https://github.com/checkstyle/checkstyle/tree/master/src/main/resources" target="_blank">start with one of the default rulesets</a> of checkstyle.</p><p>Once we have some rules, we need to add them to our projects. Therefore right click on a project and select <i>Checkstyle/Activate Checkstyle</i>. This will add the project nature and a builder. To make use of our common ruleset, create a file <i><project>/.checkstyle</i> with following content.</p>
<pre class="brush: xml"><?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
<local-check-config name="Skills Checkstyle" location="/yourProject.releng/checkstyle/checkstyle_rules.xml" type="project" description="">
<additional-data name="protect-config-file" value="false"/>
</local-check-config>
<fileset name="All files" enabled="true" check-config-name="Skills Checkstyle" local="true">
<file-match-pattern match-pattern=".java$" include-pattern="true"/>
</fileset>
</fileset-config></pre>
<p>Make sure to adapt the <i>name</i> and <i>location</i> attributes of <i>local-check-config</i> according to your project structure.</p><p>Checkstyle will now run automatically on builds or can be triggered manually via the context menu: <i>Checkstyle/Check Code with Checkstyle</i>.</p><p><b><u>Step 2: Modifying Rules</u></b></p><p>While we had to do our setup manually, we can now use the UI integration to adapt our rules. Select the <i>Properties</i> context entry from a project and navigate to <i>Checkstyle</i>, page <i>Local Check Configurations</i>. There select your ruleset and click <i>Configure...</i> The following dialog allows to add/remove rules and to change rule properties. All your changes are backed by our <i>checkstyle_rules.xml</i> file we created earlier.</p><p><b><u>Step 3: Maven Integration</u></b></p><p>We need to add the <a href="https://maven.apache.org/plugins/maven-checkstyle-plugin/" target="_blank">Maven Checkstyle Plugin</a> to our build. Therefore add following section to your master pom:</p>
<pre class="brush: xml"> <properties>
<maven.checkstyle.version>3.1.1</maven.checkstyle.version>
</properties>
<build>
<plugins>
<!-- enable checkstyle code analysis -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven.checkstyle.version}</version>
<configuration>
<configLocation>../../releng/yourProject.releng/checkstyle/checkstyle_rules.xml</configLocation>
<linkXRef>false</linkXRef>
</configuration>
<executions>
<execution>
<id>checkstyle-integration</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</pre><div><br /></div>
<p>In the <i>configuration</i> we address the ruleset we also use for the IDE plugin. Make sure that the relative path fits to your project setup. In the provided setup execution is bound to the <a href="https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference" target="_blank">verify phase</a>.</p><p><b><u>Step 4: File Exclusions</u></b></p><p>Excluding files has to be handled differently for IDE and Maven. The Eclipse plugin allows to define inclusions and exclusions via <i>file-match-pattern</i> entries in the <i>.checkstyle</i> configuration file. To exclude a certain package use:</p>
<pre class="brush: xml"> <fileset name="All files" enabled="true" check-config-name="Skills Checkstyle" local="true">
...
<file-match-pattern match-pattern="org.yourproject.generated.package.*$" include-pattern="false"/>
</fileset></pre>
<p>In maven we need to add exclusions via the plugin <i>configuration</i> section. Typically such exclusions would go to the pom of a specific project and not the master pom:</p>
<pre class="brush: xml"> <build>
<plugins>
<!-- remove generated resources from checkstyle code analysis -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven.checkstyle.version}</version>
<configuration>
<excludes>**/org/yourproject/generated/package/**/*</excludes>
</configuration>
</plugin>
</plugins>
</build></pre>
<p><b><u>Step 5: Jenkins Integration</u></b></p><p>If you followed my previous tutorials on code checkers, then this is business as usual: use the <a href="https://plugins.jenkins.io/warnings-ng/" target="_blank">warnings-ng plugin</a> on Jenkins to track our findings:</p>
<pre class="brush: xml"> recordIssues tools: [checkStyle()]</pre>
<p><a href="https://ci.eclipse.org/skills/job/skills.build.nightly/lastSuccessfulBuild/analysis/" target="_blank">Try out the live chart</a> on the skills project.</p>Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com1tag:blogger.com,1999:blog-3460102793186140898.post-90360993468233036802020-11-24T12:00:00.001+01:002020-11-24T19:01:13.956+01:00Add SpotBugs support to Eclipse, Maven, and Jenkins<p><a href="https://spotbugs.github.io/" target="_blank">SpotBugs</a> (successor of FindBugs) is a tool for static code analysis, similar like <a href="https://pmd.github.io/" target="_blank">PMD</a>. Both tools help to detect bad code constructs which might need improvement. As they partly detect different issues, they may be well combined and used simultaneously.</p><p><b><u>Step 1: Add Eclipse IDE Support</u></b></p><p>The <a href="https://spotbugs.readthedocs.io/en/latest/eclipse.html" target="_blank">SpotBugs Eclipse Plugin</a> can be installed directly via the <a href="https://marketplace.eclipse.org/content/spotbugs-eclipse-plugin" target="_blank">Eclipse Marketplace</a>.</p><p>After installation projects can be configured to use it from the projects <i>Properties</i> context menu. Navigate to the <i>SpotBugs</i> category and enable all checkboxes on the main site. Further set <i>Minimum rank to report</i> to <i>20</i> and <i>Minimum confidence to report</i> to <i>Low</i>.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYXA287DXHw39onNsXiZ0JYhAAL0ywfmU6E4-cnHaaDX25gMWNTMLWCWikFyKSN8uo6xz-JI8si8btnXR_kHc-bpIRIlZJZlEwy6zmNmvLPH2h2z6LSYH-SBLESf9jvAcKLzMDA3DMgjSm/s908/SpotBugs_prefs.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="653" data-original-width="908" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYXA287DXHw39onNsXiZ0JYhAAL0ywfmU6E4-cnHaaDX25gMWNTMLWCWikFyKSN8uo6xz-JI8si8btnXR_kHc-bpIRIlZJZlEwy6zmNmvLPH2h2z6LSYH-SBLESf9jvAcKLzMDA3DMgjSm/w400-h288/SpotBugs_prefs.png" width="400" /></a></div><p>Once done SpotBugs immediately scans the project for problems. Found issues are displayed as custom markers in editors. Further they are visible in the <i>Bug Explorer</i> view as well as in the <i>Problems</i> view.</p><p>SpotBugs also comes with a label decoration on elements in the <i>Package Explorer</i>. If you do not like these then disable all <i>Bug count decorator</i> entries in <i>Preferences/General/Appearance/Label Decorations</i>.</p><p><b><u>Step 2: Maven Integration</u></b></p><p>Integration is done via the <a href="https://spotbugs.github.io/spotbugs-maven-plugin/index.html" target="_blank">SpotBugs Maven Plugin</a>. To enable, add following section to your master pom:</p>
<pre class="brush: xml"> <properties>
<maven.spotbugs.version>4.1.4</maven.spotbugs.version>
</properties>
<build>
<plugins>
<!-- enable spotbugs code analysis -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>${maven.spotbugs.version}</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<fork>false</fork>
</configuration>
<executions>
<execution>
<id>spotbugs-integration</id>
<phase>verify</phase>
<goals>
<goal>spotbugs</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build></pre>
<p>The execution entry takes care that the <a href="https://spotbugs.github.io/spotbugs-maven-plugin/spotbugs-mojo.html" target="_blank">spotbugs goal</a> is automatically executed during the <a href="https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference" target="_blank">verify phase</a>. If you remove the execution section you would have to call the <i>spotbugs</i> goal separately:</p>
<pre class="brush: xml">mvn spotbugs:spotbugs</pre>
<p><b><u>Step 3: File Exclusions</u></b></p><p>You might have code that you do not want to get checked (eg generated files). Exclusions need to be defined in an xml file. A simple filter on package level looks like this:</p>
<pre class="brush: xml"><?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<!-- skip EMF generated packages -->
<Match>
<Package name="~org\.eclipse\.skills\.model.*" />
</Match>
</FindBugsFilter></pre>
<p>See the documentation for a <a href="https://spotbugs.readthedocs.io/en/stable/filter.html" target="_blank">full description of filter definitions</a>.</p><p>Once defined this file can be used from the SpotBugs Eclipse plugin as well as from the maven setup.</p><p>To simplify the maven configuration we can add following profile to our master pom:</p>
<pre class="brush: xml"> <profiles>
<profile>
<!-- apply filter when filter file exists -->
<id>auto-spotbugs-exclude</id>
<activation>
<file>
<exists>.settings/spotbugs-exclude.xml</exists>
</file>
</activation>
<build>
<plugins>
<!-- enable spotbugs exclude filter -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>${maven.spotbugs.version}</version>
<configuration>
<excludeFilterFile>.settings/spotbugs-exclude.xml</excludeFilterFile>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles></pre>
<p>It gets automatically enabled when a file <i>.settings/spotbugs-exclude.xml</i> exists in the current project.</p><p><b><u>Step 4: Jenkins Integration</u></b></p><p><a href="http://codeandme.blogspot.com/2020/11/add-pmd-support-to-eclipse-and-maven.html" target="_blank">Like with PMD</a>, we again use the <a href="https://plugins.jenkins.io/warnings-ng/" target="_blank">warnings-ng plugin</a> on Jenkins to track our findings:</p>
<pre class="brush: xml"> recordIssues tools: [spotBugs(useRankAsPriority: true)]</pre>
<p><a href="https://ci.eclipse.org/skills/job/skills.build.nightly/lastSuccessfulBuild/analysis/" target="_blank">Try out the live chart</a> on the skills project.</p><p><b><u>Final Thoughts</u></b></p><p>PMD is smoother on integration as it stores its rulesets in a common file which can be shared by maven and the Eclipse plugin. SpotBugs currently requires to manage rulesets separately. Still both can be implemented in a way that users automatically get the same warnings in maven and the IDE.</p>Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-25738612015322013772020-11-20T11:53:00.005+01:002020-11-20T11:53:34.455+01:00Add Code Coverage Reports to Eclipse, Maven, and Jenkins<p>Code coverage may provide some insights in your tests. They show which classes, lines of codes, and conditional branches are called by your tests. A high percentage of coverage does not automatically mean that your tests are great - as you might not have a single assertion in your test code - but at least they can give you an impression of dark areas in your code base.</p><p>This article is heavily based on the article of Lorenzo Bettini on <a href="http://www.lorenzobettini.it/2017/02/jacoco-code-coverage-and-report-of-multiple-eclipse-plug-in-projects/" target="_blank">JaCoCo Code Coverage and Report of multiple Eclipse plug-in projects</a>, so the credits for this setup are his!</p><p><b><u>Step 1: Eclipse IDE Setup</u></b></p><p>Coverage in Java projects is typically tracked with the <a href="https://www.eclemma.org/jacoco/" target="_blank">JaCoCo library</a>. The according plugin for Eclipse is called <a href="https://www.eclemma.org/" target="_blank">EclEmma</a> and is available via the <a href="https://marketplace.eclipse.org/content/eclemma-java-code-coverage" target="_blank">Eclipse Marketplace</a>.</p><p>After installation you have a new run target </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGxy9vIwlB1qiI83F_bLvhsuu07b1ZQZHg1yNPJ0H7_aIfSuvDtp6Y7LAxLDL8uNBR6OryEuiS40Qlt1HuSZseYaBZkfR0ele3HKa8JVoRp-jJ-br5lTSM4H5oOiUCrs0W26Cc264oGtoc/s112/eclemma.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="27" data-original-width="112" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGxy9vIwlB1qiI83F_bLvhsuu07b1ZQZHg1yNPJ0H7_aIfSuvDtp6Y7LAxLDL8uNBR6OryEuiS40Qlt1HuSZseYaBZkfR0ele3HKa8JVoRp-jJ-br5lTSM4H5oOiUCrs0W26Cc264oGtoc/s0/eclemma.gif" /></a></div><p>that adds coverage information to your execution. Only thing to do is to rerun your unit tests and check out the <i>Coverage</i> view.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF7V5MiKH6Gh8PtFUODNxdWMn4UflV6wyzEhLEu9_jh79J0YnN9JsN8vGNk_bljf0FZnG81FtA3_eNUOkSt_7cBJJ05HipUf6tJlrEVyIi7NPTvEj3M_bFmELm1u5cGLP8ZZa4IqIBBYHB/s985/coverage.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="529" data-original-width="985" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF7V5MiKH6Gh8PtFUODNxdWMn4UflV6wyzEhLEu9_jh79J0YnN9JsN8vGNk_bljf0FZnG81FtA3_eNUOkSt_7cBJJ05HipUf6tJlrEVyIi7NPTvEj3M_bFmELm1u5cGLP8ZZa4IqIBBYHB/w400-h215/coverage.png" width="400" /></a></div><br /><p>Multiple coverage sessions can be combined into one. That allows to accumulate the results of multiple unit tests into a single coverage report.</p><p><b><u>Step 2: Tycho integration</u></b></p><p>For the next steps I expect that you basically followed my <a href="http://codeandme.blogspot.com/p/tycho-articles.html" target="_blank">tycho tutorials</a> and have a similar setup.</p><p>First we need to enable JaCoCo in our builds:</p>
<pre class="brush: xml"> <build>
<plugins>
<!-- enable JaCoCo code coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<configuration>
<output>file</output>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<phase>pre-integration-test</phase>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build></pre>
<p>Tycho surefire executes unit tests in the maven <a href="https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference" target="_blank">integration-test</a> phase, therefore we start the agent right before. This plugin needs to be active for any plugin of type eclipse-plugin-test (<a href="http://codeandme.blogspot.com/2012/12/tycho-build-7-plug-in-unit-tests.html" target="_blank">see tycho tutorial</a>), but it is safe to put it in the <a href="http://codeandme.blogspot.com/2012/12/tycho-build-3-creating-global-build.html" target="_blank">master pom</a> of your *.releng project.</p><p>Now each test run creates coverage reports. For analysis purposes we need to merge them into a single one. Therefore create a new <i>General/Project</i> in your workspace, named <i>*.releng.coverage</i>. In the <i>pom.xml</i> file we need to add a step to aggregate all reports into one:</p>
<pre class="brush: xml"> <build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build></pre>
<p>Afterwards we need to define dependencies for the projects containing our source code:</p>
<pre class="brush: xml"> <dependencies>
<!-- Code dependencies to show coverage on -->
<dependency>
<groupId>com.example</groupId>
<artifactId>com.example.plugin1</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>com.example.plugin2</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
...
</dependencies></pre>
<p>Further we need dependencies to our test fragments (mind the different <i>scope</i>) :</p>
<pre class="brush: xml"> <dependencies>
...
<!-- Test dependencies -->
<dependency>
<groupId>com.example</groupId>
<artifactId>com.example.project1.test</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>com.example.project2.test</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
...
</dependencies></pre>
<p>If unsure, <a href="https://git.eclipse.org/c/skills/skills.git/tree/releng/org.eclipse.skills.releng.coverage/pom.xml" target="_blank">have a look at a complete pom file</a>.</p><p>Finally add the new project as a module to your master pom:</p>
<pre class="brush: xml"> <modules>
...
<module>../your.project.releng.coverage</module>
...
</modules></pre>
<p>The maven build now generates <i>*.releng.coverage/target/site/jacoco-aggregate/jacoco.xml</i> which can be picked up by various tools. Further you get a nice HTML report in the same folder for free.</p><p><b><u>Step 3: Jenkins reports</u></b></p><p>While you may directly publish the HTML report on your jenkins builds, I prefer to use the <a href="https://plugins.jenkins.io/code-coverage-api/" target="_blank">Code Coverage plugin</a>. With a single instruction in your pipeline</p>
<pre class="brush: xml"> publishCoverage adapters: [jacocoAdapter(path: 'releng/com.example.releng.coverage/target/site/jacoco-aggregate/jacoco.xml')], sourceFileResolver: sourceFiles('STORE_LAST_BUILD')</pre>
<p>it generates nice, interactive reports like these:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd4Uaoe1YjNGn_Jwpd35N0gOFwoG8S-ukEwSGzWfcNPwLfKaHM0KOAcG1Wr8EhVecU3v9m0X3qPMxf47tjp_Hfp9HdmTCPuxcU5sltsrnQI9KSn8lhiUij42AK9zkcfAxicffGhoC-VGeB/s500/echarts+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="479" data-original-width="500" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd4Uaoe1YjNGn_Jwpd35N0gOFwoG8S-ukEwSGzWfcNPwLfKaHM0KOAcG1Wr8EhVecU3v9m0X3qPMxf47tjp_Hfp9HdmTCPuxcU5sltsrnQI9KSn8lhiUij42AK9zkcfAxicffGhoC-VGeB/s320/echarts+%25281%2529.png" width="320" /></a></div><p>You may also have a look at this <a href="https://ci.eclipse.org/skills/job/skills.build.nightly/lastSuccessfulBuild/coverage/org_eclipse_skills_releng_coverage__jacoco_xml/" target="_blank">live report</a> to play around with.</p><p></p>Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-62551953079635607502020-11-18T15:41:00.003+01:002020-11-20T09:19:11.364+01:00Add PMD support to Eclipse, Maven, and Jenkins<p><a href="https://pmd.github.io/" target="_blank">PMD</a> is a static code analyzer that checks your source for problematic code constructs, design patterns, and code style.</p><p>The code smells reported on grown projects might be huge at first, but PMD allows to customize its rules and to adapt them to your needs.</p><p><b><u>Step 1: Add PMD support to Eclipse</u></b></p><p>I am using <a href="https://eclipse-pmd.github.io/" target="_blank">eclipse-pmd</a> which can be installed from the <a href="https://marketplace.eclipse.org/content/eclipse-pmd" target="_blank">Eclipse Marketplace</a>.</p><p><b><u>Step 2: Define a ruleset</u></b></p><p>PMD needs a ruleset to run against. It is stored as an xml file and can be global, workspace specific or project specific. The choice is up to you. For eclipse projects I typically have a "releng" project to host all my configuration files.</p><p>A default ruleset looks like this:</p>
<pre class="brush: xml"><?xml version="1.0"?>
<ruleset name="Custom Rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>custom ruleset</description>
<rule ref="category/java/bestpractices.xml" />
<rule ref="category/java/codestyle.xml" />
<rule ref="category/java/design.xml" />
<rule ref="category/java/documentation.xml" />
<rule ref="category/java/errorprone.xml" />
<rule ref="category/java/multithreading.xml" />
<rule ref="category/java/performance.xml" />
<rule ref="category/java/security.xml" />
</ruleset>
</pre>
<p>Store your ruleset somewhere in your workspace or on your file system.</p><p><b><u>Step 3: Enable PMD on project level</u></b></p><p>Right click on a project in your Eclipse workspace and select <i>Properties</i>. In <i>PMD</i> section check <i>Enable PMD for this project</i> and <i>Add...</i> the ruleset file stored before. The <i>Name</i> is not important and can be freely chosen.</p><p>Your rules are live now and PMD should immediately start to add warnings to your code and the <i>Problems</i> view.</p><p><b><u>Step 4: Refine your rules</u></b></p><p>The default ruleset might report some issues you do want to treat differently in your project. Therefore you may change rules by setting parameters or disable unwanted rules at all. To alter a rule, you first have to find it in the <a href="https://pmd.github.io/pmd-6.29.0/pmd_rules_java.html" target="_blank">list of available rules</a>. For disabling you just need to add an exclude node to your rule settings file, eg:</p>
<pre class="brush: xml"> <rule ref="category/java/bestpractices.xml">
<!-- logger takes care of guarding -->
<exclude name="GuardLogStatement" />
</rule>
</pre>
<p>Configuring a rule can be done like this:</p>
<pre class="brush: xml"> <rule ref="category/java/codestyle.xml/ClassNamingConventions">
<properties>
<property name="utilityClassPattern"
value="[A-Z][a-zA-Z0-9]+" />
</properties>
</rule>
</pre>
<p>A full working ruleset as used by one of my projects can be <a href="https://git.eclipse.org/c/skills/skills.git/tree/releng/org.eclipse.skills.releng/pmd/pmd_rules.xml" target="_blank">viewed online</a>.</p><p>Whenever you change your ruleset you need to recompile your project to get these rules applied. You may do so by selecting <i>Project/Clean...</i> from the main menu.</p><p><b><u>Step 5: Maven integration</u></b></p><p>Integration is done by the <a href="https://maven.apache.org/plugins/maven-pmd-plugin/" target="_blank">maven-pmd-plugin</a>. Just add following section to your pom:</p>
<pre class="brush: xml"> <build>
<plugins>
<!-- enable PMD code analysis -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<linkXRef>false</linkXRef>
<rulesets>path/to/your/ruleset.xml</rulesets>
</configuration>
</plugin>
</plugins>
</build>
</pre>
<p>Make sure to adapt the path to your ruleset accordingly.</p><p>Afterwards run your build using</p>
<pre class="brush: javascript">mvn pmd:pmd pmd:cpd
</pre>
<p>If you use the <a href="https://maven.apache.org/plugins/maven-site-plugin/" target="_blank">maven-site-plugin</a>, you may additionally generate html reports of PMD findings.</p><p><b><u>Step 6: Jenkins integration</u></b></p><p>Static reports are nice, but charts over time/commits are even better. In case you use Jenkins you may have a look at the <a href="https://plugins.jenkins.io/warnings-ng/" target="_blank">warnings-ng plugin</a>. When you generate yout pmd.xml files via maven, this plugin can pick them up and draw nice reports. In a pipeline build this only needs one line:</p>
<pre class="brush: javascript">recordIssues(tools: [cpd(), pmdParser()])
</pre>
<p>to get charts like these:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI7UHJeAwoL741Ai-NuNGttUTLhjZzfatz89NajpLWj4PcKKGwaWa5jh1Mo5BmTRY-sWxLBHHt6XRHSzxEIrBUJQTUJLWUiI2mD3rmG8S4aMtlO2rkcB1isNQ_AH5Kqj-NeMCay8JRYISz/s817/warnings-ng.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="817" data-original-width="805" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI7UHJeAwoL741Ai-NuNGttUTLhjZzfatz89NajpLWj4PcKKGwaWa5jh1Mo5BmTRY-sWxLBHHt6XRHSzxEIrBUJQTUJLWUiI2mD3rmG8S4aMtlO2rkcB1isNQ_AH5Kqj-NeMCay8JRYISz/w394-h400/warnings-ng.png" width="394" /></a></div><br /><p><a href="https://ci.eclipse.org/skills/job/skills.build.nightly/lastSuccessfulBuild/analysis/" target="_blank">Try out the live chart</a> on the skills project.</p><p>Finally the plugin even allows to compare the amount of issues against a baseline. This allows to add quality gates, eg to fail the build in case your issue count increases. I strongly encourage to enforce such rules. Otherwise warnings are nice but do get ignored by everybody.</p><p><br /></p>Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com1tag:blogger.com,1999:blog-3460102793186140898.post-36938895570826660102019-11-18T12:11:00.000+01:002019-11-18T12:19:24.897+01:00Jakarta Microprofile REST Client in EclipseToday we are going to implement a simple REST client for an Eclipse RCP application. Now with Jakarta @ Eclipse and all these nice Microprofile implementations this should be a piece of cake, right? Now lets see...<br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/RESTEasy_client.zip?raw=true" target="_blank">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/RESTEasy_client.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/rest_client" target="_blank">browse the files</a> online.<br />
<br />
<b><u>Step 1: Dependencies</u></b><br />
<br />
The <a href="https://github.com/eclipse/microprofile-rest-client" target="_blank">Eclipse Microprofile REST Client repository</a> is a good place to get started. It points to several implementations (at the bottom of the readme). Unfortunately these implementations do not host any kind of p2 sites which we could use directly. So our next stop is <a href="https://download.eclipse.org/tools/orbit/downloads/" target="_blank">Eclipse Orbit</a>, but same situation there. This means we need to collect our dependencies manually.<br />
<br />
For my example I used <a href="https://resteasy.github.io/" target="_blank">RESTEasy</a>, simply as it was the only one I could get working within reasonable time. To fetch dependencies, download the <a href="https://resteasy.github.io/downloads.html" target="_blank">latest version of RESTEasy</a>. As the RESTEasy download package does not contain the REST client API, we need to fetch that from another source. I found it in the <a href="http://cxf.apache.org/download.html" target="_blank">Apache CXF project</a>, so download the latest version too. If you know a better source, please let me know in the comments.<br />
<br />
Now create a new <i>Plug-in from Existing JAR Archives</i>. Click on <i>Add External...</i> and add all jars from resteasy-jaxrs-x.y.z.Final/lib/*.jar. Further add apache-cxf-x.y.z/lib/jakarta.ws.rs-api-x.y.z.jar.<br />
This plug-in now contains all dependencies we need for our client. Unfortunately also a lot of other stuff we probably do not need, but we leave the cleanup for later.<br />
<br />
<b><u>Step 2: Define the REST service</u></b><br />
<br />
For our example we will build a client for the <a href="https://petstore.swagger.io/" target="_blank">Petstore Service</a>, which can be used for testing purposes. Further it provides a swagger interface to test the REST calls online. I recommend to check out the API and play with some commands online and with curl.<br />
<br />
Lets write a simple client for the store with its 4 commands. The simplest seems to be the <i>inventory</i> command, so we will start there. Create a new Java interface:<br />
<pre class="brush: javascript">package com.codeandme.restclient.resteasy;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public interface IStoreService {
@GET
@Path("/v2/store/inventory")
@Produces(MediaType.APPLICATION_JSON)
Response getInventory();
}</pre>
Everything necessary for RESTEasy is provided via annotations:<br />
<br />
<ul>
<li><i>@Path</i> defines the path for the command of the REST service</li>
<li><i>@GET</i> defines that we have to use a GET command (there exist annotations for POST, DELETE, PUT)</li>
<li><i>@Produces</i> finally defines the type of data we do get in response from the server.</li>
</ul>
<div>
<b><u>Step 3: Create an instance of the service</u></b><br />
<br />
Create a new class <i>StoreServiceFactory</i>:</div>
<pre class="brush: javascript">package com.codeandme.restclient.resteasy;
import java.net.URI;
import java.net.URISyntaxException;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
public class StoreServiceFactory {
public static IStoreService createStoreService() throws URISyntaxException {
ResteasyClient client = new ResteasyClientBuilderImpl().build();
ResteasyWebTarget target = client.target(new URI("https://petstore.swagger.io/"));
return target.proxy(IStoreService.class);
}
}
</pre>
<div>
<br /></div>
This is the programmatic way to create a client instance. There also exists another method called <a href="https://download.eclipse.org/microprofile/microprofile-rest-client-1.3/microprofile-rest-client-1.3.html#restcdi" target="_blank">CDI</a>, which I did not try out in Eclipse.<br />
<br />
The service is ready and usable, so give it a try. The result object returned does contain some valuable information:<br />
<br />
<ul>
<li><i>getStatus()</i> provides the H<a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes" target="_blank">TTP response status</a>. 200 is expected for a successful <i>getInventory()</i></li>
<li><i>getEntity()</i> provides an <i>InputStream</i> which contains the JSON encoded response data from the server</li>
</ul>
<div>
<b><u>Step 4: Response decoding</u></b></div>
<div>
<br /></div>
<div>
Our response is encoded as <a href="https://www.tutorialspoint.com/json/json_data_types.htm" target="_blank">JSON collection of properties</a>. In Java terms this basically reflects to a <i>Map<String, String></i>. Instead of decoding the data manually, we let the framework do it for us:</div>
<div>
<br /></div>
<div>
Change the <i>IStoreService</i> to:</div>
<div>
<br /></div>
<pre class="brush: javascript"> Map<String, String> getInventory();</pre>
Anything else is done by the framework. Now how easy was that?<br />
<br />
<b><u>Step 5: POST request</u></b><br />
<br />
To place an order we need order parameters. Best we encapsulate them in a dedicated <a href="https://github.com/Pontesegger/codeandme/blob/master/rest_client/com.codeandme.restclient.resteasy/src/com/codeandme/restclient/resteasy/Order.java" target="_blank">Order class</a>. From the definition of the <i>order</i> REST call we can see that we need following class properties: <i>id, petId, quantity, shipDate, status, complete</i>. Add these parameters as fields to the <i>Order</i> class and create getters/setters for them.<br />
<br />
Now we can extend our <i>IStoreService</i> with the <i>fileOrder()</i> call:<br />
<br />
<br />
<pre class="brush: javascript">@Path("/v2/store")
public interface IStoreService {
@GET
@Path("inventory")
@Produces(MediaType.APPLICATION_JSON)
Map<String, String> getInventory();
@POST
@Path("order")
@Consumes(MediaType.APPLICATION_JSON)
void fileOrder(Order order);
}</pre>
<br />
The <i>Order</i> automatically gets encoded as JSON object. No need for us to do the coding manually!<br />
<br />
As parts of the path are the same for both calls, I moved the common component to the class level.<br />
<br />
<b><u>Step 6: Path parameters</u></b><br />
<br />
To fetch an order we need to put the <i>orderId</i> in the request path. Coding of such parameters is put in curly braces. The parameter on the java call then gets annotated so the framework knows which parameter value to put into the path:<br />
<br />
<pre class="brush: javascript"> @GET
@Path("order/{orderId}")
@Produces(MediaType.APPLICATION_JSON)
Order getOrder(@PathParam("orderId") int orderId);
</pre>
<div>
<br /></div>
Again the framework takes care of the decoding of the JSON data.<br />
<br />
<b><u>Step 7: DELETE an Order</u></b><br />
<br />
Deleting needs the <i>orderId</i> as before:<br />
<br />
<pre class="brush: javascript"> @DELETE
@Path("order/{orderId}")
void deleteOrder(@PathParam("orderId") int orderId);
</pre>
<div>
<br /></div>
The REST API does not provide a useful JSON response to the delete call. One option is to leave the response type to <i>void</i>. In case the command fails, an exception will be thrown (eg when the <i>orderId</i> is not found and the server returns 404).<br />
<br />
Another option is to set the return type to <i>javax.ws.rs.core.Response</i>. Now we do get everything the server sends back and no execption is thrown anymore. Sometimes we might only be interested in the status code. This can be fetched when setting the return type to <i>Response.Status</i>. Again, no exception will be thrown on a 404.<br />
<br />
<b><u>Optional: Only have required RESTEasy dependencies</u></b><br />
<br />
Looking at all these jars I could not figure out a good way to get rid of the ones unused by the REST client. So I provided unit tests for all my calls and then removed dependencies step by step until I found the minimal set of required jars.<br />
<br />
<br />
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-3906686087382438312019-07-08T20:52:00.001+02:002019-07-08T20:54:52.208+02:00Building UIs with EASEYou probably used <a href="https://www.eclipse.org/ease/" target="_blank">EASE</a> before to automate daily tasks in your IDE or to <a href="http://codeandme.blogspot.com/2014/12/ease-scripts-conquer-ui.html" target="_blank">augment toolbars and menus</a> with custom functionality. But so far scripts could not be used to build UIs. This changed with the recent contribution of the <i>UI Builder</i> module.<br />
<br />
<b><u>What it is all about</u></b><br />
The <i>UI Builder</i> module allows to create views and dialogs by pure script code in your IDE. It is great for custom views that developers do not want to put into their products, for rapid prototyping and even for mocking.<br />
<br />
The aim of EASE is to hide layout complexity as much as possible and provide a simple, yet flexible way to implement typical UI tasks.<br />
<br />
<b><u>Example 1: Input Form</u></b><br />
We will start by creating a simple input form for address data.<br />
<br />
<pre class="brush: javascript">loadModule("/System/UI Builder");
createView("Create Contact");
setColumnCount(2);
createLabel("First Name:");
var txtFirstName = createText();
createLabel("Last Name:");
var txtLastName = createText();
</pre>
This snippet will create a dynamic view as shown below:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdXJ4CpAVE_xmOKmpaMmfAJk_4qeOpTbtpHb75mXCrAj34-_M8BylCNX9C8Ag2MZr8q-nKFm_3J1CcPrZjdLOEorP_47pijKFlEqOy0YEeHtRSbqt9EDQMIbWRX2WOfr38yuUWjJPuo0cH/s1600/script_form.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="172" data-original-width="443" height="155" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdXJ4CpAVE_xmOKmpaMmfAJk_4qeOpTbtpHb75mXCrAj34-_M8BylCNX9C8Ag2MZr8q-nKFm_3J1CcPrZjdLOEorP_47pijKFlEqOy0YEeHtRSbqt9EDQMIbWRX2WOfr38yuUWjJPuo0cH/s400/script_form.png" width="400" /></a></div>
The renderer used will apply a <a href="https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm" target="_blank">GridLayout</a>. By setting the <i>columnCount</i> to 2 we may simply add our elements without providing any additional layout information - a simple way to create basic layouts.<br />
<br />
If needed EASE provides more control by providing layout information when creating components:<br />
<br />
<pre class="brush: javascript">createView("Create Contact");
createLabel("First Name:", "1/1 >x");
var txtFirstName = createText("2-4/1 o!");
createLabel("Last Name:", "1/2 >x");
var txtLastName = createText("2-4/2 o!");
</pre>
Creates the same view as above, but now with detailed layout information.<br />
As an example <i>"1/2 >x"</i> means: first column, second row, horizontal align right, vertical align middle. A full documentation on the syntax is provided in the module documentation (Hover over the <i>UI Builder</i> module in the <i>Modules Explorer</i> view).<br />
<br />
Now lets create a combo viewer to select a country for the address:<br />
<pre class="brush: javascript">cmbCountry = createComboViewer(["Austria", "Germany", "India", "USA"])
</pre>
Simple, isn't it?<br />
<br />
So far we did not need to react on any of our UI elements. Next step is to create a button which needs some kind of callback action:<br />
<pre class="brush: javascript">createButton("Save 1", 'print("you hit the save button")')
createButton("Save 2", saveAddress)
function saveAddress() {
print("This is the save method");
}
</pre>
The first version of a button adds the callback code as string argument. When the button gets pressed, the callback code will be executed. You may call any script code that the engine is capable of interpreting.<br />
<br />
The second version looks a bit cleaner, as it defines a function <i>saveAddress() </i>which is called on a button click. Note that we provide a function reference to <i>createButton()</i>.<br />
<br />
<a href="https://git.eclipse.org/c/ease/org.eclipse.ease.scripts.git/tree/Demos/UI%20Builder%20Examples/AddressBook/Address%20Input.js" target="_blank">View the full example</a> of this script on our <a href="https://git.eclipse.org/c/ease/org.eclipse.ease.scripts.git/tree" target="_blank">script repository</a>. In addition to some more layouting it also contains a working implementation of the save action to store addresses as JSON data files.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9r7CJ1uGpBXgWS1c5bmVedh_6KQ8PFDLGrrzMAhn2E-yNrIXrtqpYf0pwf2IW1hSoBlXeVb-LWbPbRJBCCqiCBfzdWgn7RjoyQbVNfLnpwHjgHsUNz8k9bTbQaBm9nwVqBQLHz0cf-V6R/s1600/script_form2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="274" data-original-width="518" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9r7CJ1uGpBXgWS1c5bmVedh_6KQ8PFDLGrrzMAhn2E-yNrIXrtqpYf0pwf2IW1hSoBlXeVb-LWbPbRJBCCqiCBfzdWgn7RjoyQbVNfLnpwHjgHsUNz8k9bTbQaBm9nwVqBQLHz0cf-V6R/s400/script_form2.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<b><u>Interacting with SWT controls</u></b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The saveAddress() method needs to read data from the input fields of our form. This could be done using</div>
<pre class="brush: javascript">var firstName = txtFirstName.getText();</pre>
Unfortunately SWT Controls can only be queried in the UI thread, while the script engine is executed in its own thread. To move code execution to the UI thread, the UI module provides a function executeUI(). By default this functionality is disabled as a bad script executed in the UI thread might stall your Eclipse IDE. To enable it you need to set a checkbox in <i>Preferences/Scripting</i>. The full call then looks like this:<br />
<pre class="brush: javascript">loadModule("/System/UI")
var firstName = executeUI('txtFirstName.getText();');</pre>
<br />
<b><u>Example 2: A viewer for our phone numbers</u></b><br />
<br />
Now that we are able to create some sample data, we also need a viewer for our phone numbers. Say we are able to load all our addresses into an array, the only thing we need is a table viewer to visualize our entries. Following 2 lines will do the job:<br />
<pre class="brush: javascript">var addresses = readAddresses();
var tableViewer = createTableViewer(addresses)
</pre>
Where <i>readAddresses()</i> collects our <i>*.address</i> files and stores them into an array.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRR1CbCBydzI6wF1g9ySYSGcdzd-_JkrEn_edvrKP-7w1YNAhXuh-yaALpjgKOc7dDrRtVflPkw00p6I32j_04HDOvtH0O2FADnoptTjU2M4MoVDps6OlGHQNeErZwnkXWZTvUkcOC1WmJ/s1600/phonebook_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="207" data-original-width="612" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRR1CbCBydzI6wF1g9ySYSGcdzd-_JkrEn_edvrKP-7w1YNAhXuh-yaALpjgKOc7dDrRtVflPkw00p6I32j_04HDOvtH0O2FADnoptTjU2M4MoVDps6OlGHQNeErZwnkXWZTvUkcOC1WmJ/s400/phonebook_1.png" width="400" /></a></div>
So the viewer works, however we need to define how our columns shall be rendered.<br />
<pre class="brush: javascript">createViewerColumn(tableViewer, "Name", createLabelProvider("getProviderElement().firstName + ' ' + getProviderElement().lastName"))
createViewerColumn(tableViewer, "Phone", createLabelProvider("getProviderElement().phone"))
</pre>
Whenever a callback needs a viewer element, getProviderElement() holds the actual element.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj31HBUu9OR2Rmk6SfIT5hKGDu7Q8VK_z7NU6g_d2230IbT6PSGsrG9URO8uXfWiJlqL9pD8qTNr3uEDv8uf5R-wkvVOMBwPv4WNTLoMDhcPtsB9mlH_BSByMCZKXb9CD3RSzDp8R_TgeHk/s1600/phonebook_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="208" data-original-width="612" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj31HBUu9OR2Rmk6SfIT5hKGDu7Q8VK_z7NU6g_d2230IbT6PSGsrG9URO8uXfWiJlqL9pD8qTNr3uEDv8uf5R-wkvVOMBwPv4WNTLoMDhcPtsB9mlH_BSByMCZKXb9CD3RSzDp8R_TgeHk/s400/phonebook_2.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
We are done! 3 lines of code for a TableViewer does not sound too bad, right? Again a <a href="https://git.eclipse.org/c/ease/org.eclipse.ease.scripts.git/tree/Demos/UI%20Builder%20Examples/AddressBook/List.js" target="_blank">full example</a> is available on our script repository. It automatically loads <i>*.address</i> files from your workspace and displays them in the view.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<b><u>Example 3: A workspace viewer</u></b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
We had a <i>TableViewer</i> before, now lets try a <i>TreeViewer</i>. As a tree needs structure, we need to provide a callback to calculate child elements from a given parent:</div>
<pre class="brush: javascript">var viewer = createTreeViewer(getWorkspace().getProjects(), getChildren);
function getChildren() {
if (getProviderElement() instanceof org.eclipse.core.resources.IContainer)
return getProviderElement().members();
return null;
}</pre>
So simple! The <a href="https://git.eclipse.org/c/ease/org.eclipse.ease.scripts.git/tree/Demos/UI%20Builder%20Examples/Project%20Info%20Viewer.js" target="_blank">full example</a> looks like this:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7c8TJRGk44tdRPxDKWhzOFBn-VKb21qXkQX_HGp9NsKabYAql98_ynIlrW2sexDtVGP96bxpenrbBaoGL2m7laXaK40w37T10bQE_CVTHVEGvCsqcYwSSOSUZPch51LR6NS0kE9QRph1n/s1600/project_viewer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="612" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7c8TJRGk44tdRPxDKWhzOFBn-VKb21qXkQX_HGp9NsKabYAql98_ynIlrW2sexDtVGP96bxpenrbBaoGL2m7laXaK40w37T10bQE_CVTHVEGvCsqcYwSSOSUZPch51LR6NS0kE9QRph1n/s1600/project_viewer.png" /></a></div>
<b><u>Example 4: Math function viewer</u></b><br />
<br />
The last example demonstrates how to add a custom <i>Control</i> to a view.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGkPxXmejtfDgAfKsIMn_Q3_prJY4r3HCx4Mq9NZ70SuOAdOleVrs98D1IzvhPBcF0Uxskv7Xl_lSQrdbx6afEzm9342Et52sTW2AOfg2glu-q9OmdHcHn_nCTJsoFIKx9mArUBk2glTOA/s1600/functions.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="517" data-original-width="1088" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGkPxXmejtfDgAfKsIMn_Q3_prJY4r3HCx4Mq9NZ70SuOAdOleVrs98D1IzvhPBcF0Uxskv7Xl_lSQrdbx6afEzm9342Et52sTW2AOfg2glu-q9OmdHcHn_nCTJsoFIKx9mArUBk2glTOA/s640/functions.png" width="640" /></a></div>
For plotting we use the Charting module that is shipped with EASE. The <a href="https://git.eclipse.org/c/ease/org.eclipse.ease.scripts.git/tree/Demos/UI%20Builder%20Examples/Plot%20function.js" target="_blank">source code</a> should be pretty much self explanatory.<br />
<br />
<b><u>Some Tips & Tricks</u></b><br />
<br />
<ul>
<li>Layouting is dynamic.<br />Unlike the Java <i>GridLayout</i> you do not need to fill all cells of your layout. The EASE renderer takes care to automatically fill empty cells with placeholders<br /></li>
<li>Elements can be replaced.<br />If you use coordinates when creating controls, you may easily replace a given control by another one. This simplifies the process of layouting (eg if you experience with alignments) and even allows a view to dynamically change its components depending on some external data/events<br /></li>
<li>Full control.<br />While some methods from SWT do not have a corresponding script function, still all SWT calls may be used as the <i>create*</i> methods expose the underlying SWT instances.<br /></li>
<li>Layout help.<br />To simplify layouting use the <i>showGrid()</i> function. It displays cell borders that help you to see row/column borders.</li>
</ul>
<br />
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com1tag:blogger.com,1999:blog-3460102793186140898.post-67332933494929152462019-03-25T13:31:00.001+01:002019-03-25T13:31:12.784+01:00JFace TableViewer sorting via Drag and DropRecently I wanted to sort elements in a <i>TableViewer</i> via drag and drop and was astonished that I could not find existing helper classes or tutorial for this fairly trivial use case. So here is one for you in case you got the same use case.<br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/TableViewer_DnD_Sorting.zip?raw=true" target="_blank">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/TableViewer_DnD_Sorting.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/ui/TableViewer%20DnD%20Sorting" target="_blank">browse the files</a> online.<br />
<br />
If you are just interested in the helper class, have a look at <a href="https://github.com/Pontesegger/codeandme/blob/master/ui/TableViewer%20DnD%20Sorting/src/com/codeandme/tableviewer/dnd/sorting/DnDSortingSupport.java" target="_blank">DnDSortingSupport</a>.<br />
<br />
<b><u>Prerequisites:</u></b><br />
<br />
To have something to work on I will start with a <i>TableViewer</i> containing some data stored in a <i>java.util.List</i>. It is a default <i>TableViewer</i> and therefore I expect you have something similar ready for your experiments.<br />
<br />
<b><u>Step 1: Add drag support</u></b><br />
<br />
<a href="https://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html" target="_blank">Drag and Drop support for SWT</a> is implemented via <i>DragSource</i> and <i>DropTarget</i> instances. To define that we can drag data, we need to bind a <i>DragSource</i> to a <i>Control</i>.
<br />
<pre class="brush: java"> DragSource dragSource = new DragSource(tableViewer.getControl(), DND.DROP_MOVE);
dragSource.setTransfer(LocalSelectionTransfer.getTransfer());
dragSource.addDragListener(new DragSourceAdapter() {
@Override
public void dragStart(DragSourceEvent event) {
event.doit = !tableViewer.getStructuredSelection().isEmpty();
}
@Override
public void dragSetData(DragSourceEvent event) {
if (LocalSelectionTransfer.getTransfer().isSupportedType(event.dataType)) {
LocalSelectionTransfer.getTransfer().setSelection(tableViewer.getStructuredSelection());
LocalSelectionTransfer.getTransfer().setSelectionSetTime(event.time & 0xFFFF);
}
}
@Override
public void dragFinished(DragSourceEvent event) {
LocalSelectionTransfer.getTransfer().setSelection(null);
LocalSelectionTransfer.getTransfer().setSelectionSetTime(0);
}
});
</pre>
<div>
<br /></div>
In line 1 we create the <i>DragSource</i> and define allowed DnD operations. As we want to sort elements, we only allow <i>DND.MOVE</i> operations. Then we define the way data gets transferred from the <i>DragSource</i> to the <i>DropTarget</i>. As we stay within the same Eclipse application we may use a <i>LocalSelectionTransfer</i>.<br />
<br />
The first thing that happens on a drag is <i>dragStart()</i>. Technically the selection cannot be empty as we have to select something before we start the operation, so this implementation is merely to understand how we could deny the operation right from the start.<br />
<br />
After the drop operation got accepted in the <i>DropTarget</i> (see below) we get asked to <i>dragSetData()</i> and define what data we are moving. <i>setSelectionSetTime()</i> is not needed by our <i>DropTarget</i>, so again this is for completeness only.<br />
<br />
Finally we need to clean up after the operation is done.<br />
<br />
<b><u>Step 2: Add drop support</u></b><br />
<br />
Implementation is similar like before, just now we need a <i>DropTarget</i>. Instead of writing our own <i>DropTargetListener</i> we may use a <i>ViewerDropAdapter</i> which covers most of the required work already.<br />
<pre class="brush: java"> DropTarget dropTarget = new DropTarget(tableViewer.getControl(), DND.DROP_MOVE);
dropTarget.setTransfer(LocalSelectionTransfer.getTransfer());
dropTarget.addDropListener(new ViewerDropAdapter(tableViewer) {
@Override
public void dragEnter(DropTargetEvent event) {
// make sure drag was triggered from current tableViewer
if (event.widget instanceof DropTarget) {
boolean isSameViewer = tableViewer.getControl().equals(((DropTarget) event.widget).getControl());
if (isSameViewer) {
event.detail = DND.DROP_MOVE;
setSelectionFeedbackEnabled(false);
super.dragEnter(event);
} else
event.detail = DND.DROP_NONE;
} else
event.detail = DND.DROP_NONE;
}
@Override
public boolean validateDrop(Object target, int operation, TransferData transferType) {
return true;
}
@Override
public boolean performDrop(Object target) {
int location = determineLocation(getCurrentEvent());
if (location == LOCATION_BEFORE) {
if (modelManipulator.insertBefore(getSelectedElement(), getCurrentTarget())) {
tableViewer.refresh();
return true;
}
} else if (location == LOCATION_AFTER) {
if (modelManipulator.insertAfter(getSelectedElement(), getCurrentTarget())) {
tableViewer.refresh();
return true;
}
}
return false;
}
private Object getSelectedElement() {
return ((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).getFirstElement();
}
});
</pre>
<div>
<br /></div>
<i>dragEnter()</i> is the first thing that happens on the drop part of DnD. The default implementation is already fine. Our implementation additionally checks that the drag source is our current <i>TableViewer</i>. Further we disable the selectionFeedback. The feedback visually shows the user whether we drop before an element, on the element, or after it. The <i>ViewerDropAdapter</i> already supports these kind of feedbacks. Until <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=545733" target="_blank">bug 545733</a> gets fixed the helper class contains a small patch to provide before/after feedback only. It does not make sense to drop on another element when we do sorting, right?<br />
<br />
<i>validateDrop()</i> will be queried multiple times. We might check that we do not drop the table element on itself, but we spared this check for the current example.<br />
<br />
<i>performDrop()</i> finally implements the drop operation. To keep the helper class generic I used an interface that allows to insert elements before or after another element. An implementation of it needs to be passed to the helper class.<br />
<br />
<pre class="brush: java"> public interface IModelManipulator {
boolean insertBefore(Object source, Object target);
boolean insertAfter(Object source, Object target);
}</pre>
The helper class comes with an implementation for <i>java.util.List</i>, which you may reuse.<br />
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com2tag:blogger.com,1999:blog-3460102793186140898.post-74726548096001666072018-12-18T12:27:00.001+01:002018-12-18T12:28:24.059+01:00Jenkins 7: Pipeline SupportNext step in our Jenkins tutorials is to add support for <a href="https://jenkins.io/doc/book/pipeline/getting-started/" target="_blank">pipeline builds</a>.<br />
<br />
<u><b>Jenkins Tutorials</b></u><br />
<br />
For a list of all jenkins related tutorials see <a href="https://codeandme.blogspot.com/p/jenkins-articles.html" target="_blank">Jenkins Tutorials Overview.</a><br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_07_pipeline.zip?raw=true" target="_blank">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_07_pipeline.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/07_pipeline" target="_blank">browse the files</a> online.<br />
<br />
<b><u>Step 1: Adjusting the code</u></b><br />
<br />
Good news first: our code (including jelly) from the <a href="https://codeandme.blogspot.com/2018/12/jenkins-6-advanced-configuration-area.html" target="_blank">previous tutorial</a> is ready to be used in pipeline without change. There are some <a href="https://jenkins.io/doc/developer/plugin-development/pipeline-integration/" target="_blank">considerations to be taken</a> when writing pipeline plugins, but we already took care of this.<br />
<br />
In pipeline each build step needs a name to be addressed. By default this would be the class name and a call would look like this:<br />
<pre class="brush: java">step([$class: 'HelloBuilder', buildMessage: 'Hello from pipeline'])</pre>
<br />
When using the <i>@Symbol</i> annotation on the <i>Descriptor</i> we can provide a nicer name for our build step.<br />
<br />
<b><u>Step 2: Adding dependencies to the execution environment</u></b><br />
<br />
Our current test target does not support pipeline jobs as we did not add the right dependencies to the pom file so far. We will first see how to add dependencies in general, in the next step we will fix the build afterwards.<br />
<br />
To add support for pipeline, add following definition to your <i>pom.xml</i>:<br />
<pre class="brush: xml"> <dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-aggregator</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
</dependencies></pre>
<br />
We always need <i>groupId</i>, <i>artifactId</i> and a <i>version</i>. To get these parameters you would typically <a href="https://plugins.jenkins.io/workflow-aggregator" target="_blank">look up a plugin on the Jenkins plugin site</a>. Locate the link to the <a href="https://github.com/jenkinsci/workflow-aggregator-plugin" target="_blank">source code</a> (Github) and open the <a href="https://github.com/jenkinsci/workflow-aggregator-plugin/blob/master/pom.xml" target="_blank">pom.xml</a> of the corresponding plugin. There you will find definitions for <i>groupId</i> and <i>artifactId</i>.<br />
<br />
Available versions can be found on the <a href="https://repo.jenkins-ci.org/" target="_blank">Jenkins Artifactory server</a> (we added this server to our pom already). There navigate to the <i>public</i> folder, then follow the structure down, first opening folder from the <i>groupId</i> followed by the <i>artifactId</i>. For the pipeline dependency we would open <i>public/org/jenkins-ci/plugins/workflow/workflow-aggregator</i>. <a href="https://repo.jenkins-ci.org/list/public/org/jenkins-ci/plugins/workflow/workflow-aggregator/2.6/" target="_blank">Latest version at the time of writing</a> is 2.6.<br />
<br />
Setting the <i>scope</i> to <i>test</i> means that we do not have a build dependency on the plugin. Instead we need it only deployed and enabled on our test instance.<br />
<br />
When adding build dependencies you should run<br />
<pre class="brush: xml">mvn -DdownloadSources=true -DdownloadJavadocs=true -DoutputDirectory=target/eclipse-classes eclipse:eclipse</pre>
like we did in the first tutorial. It will update the <i>.classpath</i> file in your eclipse project, automatically adding required libraries to the build path. Take care that also the <i>.project</i> file gets rewritten!<br />
<br />
<b><u>Step 3: Dependency Resolution</u></b><br />
<br />
We added our dependency, so everything should be done, right?<br />
Wrong! The <i>maven-enforcer-plugin</i> <a href="https://maven.apache.org/enforcer/enforcer-rules/requireUpperBoundDeps.html" target="_blank">verifies plugin dependencies</a> for us and will detect some incompatibilities, eg:<br />
<pre class="brush: xml">Require upper bound dependencies error for org.jenkins-ci.plugins:script-security:1.39 paths to dependency are:
+-com.codeandme:builder.hello:1.0-SNAPSHOT
+-org.jenkins-ci.plugins.workflow:workflow-aggregator:2.6
+-org.jenkins-ci.plugins.workflow:workflow-support:2.20
+-org.jenkins-ci.plugins:script-security:1.39
and
+-com.codeandme:builder.hello:1.0-SNAPSHOT
+-org.jenkins-ci.plugins.workflow:workflow-aggregator:2.6
+-org.jenkins-ci.plugins.workflow:workflow-durable-task-step:2.22
+-org.jenkins-ci.plugins:script-security:1.39
and
+-com.codeandme:builder.hello:1.0-SNAPSHOT
+-org.jenkins-ci.plugins.workflow:workflow-aggregator:2.6
+-org.6wind.jenkins:lockable-resources:2.3
+-org.jenkins-ci.plugins:script-security:1.26
...</pre>
This is one of multiple dependency conflicts detected. It seems that maven at first resolves the dependency with the lowest version number. As some plugins need a newer version, we need to resolve the dependency by our own.<br />
<br />
The simplest way I found is to check for the highest version of the required plugin (in the upper case: script-security) and add it to our dependencies sections in the <i>pom.xml</i> file. I was hoping for some maven help on this process, but failed. So I ended up adding required dependencies manually until the build was satisfied.<br />
<br />
You might run into other build problems, eg after adding a <i>test</i> dependency to the <i>symbol-annotation</i> plugin, my build started to fail, not being able to resolve <i>Symbol.class</i> anymore. Reason is that <i>symbol-annotation</i> is actually a <i>build</i> dependency rather than a <i>test</i> dependency. By binding it to the <i>test</i> scope only we broke the build.<br />
<br />
Once you sorted out all dependencies (<a href="https://github.com/Pontesegger/codeandme/blob/master/jenkins/07_pipeline/builder.hello/pom.xml" target="_blank">see resulting pom.xml</a>) your test instance will be able to run pipeline jobs.<br />
<br />
<b><u>Step 4: Testing the Build Step in Pipeline</u></b><br />
<br />
On your test instance create a new <i>Pipeline</i> and add following <i>script</i>:<br />
<pre class="brush: xml">node {
stage('Greetings') {
greet buildDelay: 'long', buildMessage: 'Hello pipeline build'
}
}</pre>
Coding the command line for our build step can either be done manually or by using the <i>Pipeline Syntax</i> helper. The link is available right below the <i>Script</i> section in your job configuration. Jenkins makes use of our previous jelly definitions to display a visual helper for the <i>Hello Build</i> step. We may set all optional parameters on the UI and let Jenkins create the command line to be used in the pipeline script.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCs4AIYbJGaGUrkomoXvzKYEv1QPwBOvK579eyaGuL8c4kJkv6fRfzMP20fLr51h1cUtYAJj9XUEj9LmigYWl9H28Qy3QmHNKdCq3tiE-RskedJckC_xbPCVWpByGEraBzY7jOQTcDxuc2/s1600/jenkins.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="483" data-original-width="677" height="456" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCs4AIYbJGaGUrkomoXvzKYEv1QPwBOvK579eyaGuL8c4kJkv6fRfzMP20fLr51h1cUtYAJj9XUEj9LmigYWl9H28Qy3QmHNKdCq3tiE-RskedJckC_xbPCVWpByGEraBzY7jOQTcDxuc2/s640/jenkins.png" width="640" /></a></div>
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-14874863333788199092018-12-03T20:44:00.001+01:002018-12-03T20:44:02.900+01:00Jenkins 6: Advanced Configuration AreaOur build is advancing. Today we want to move optional fields into an advanced section and provide reasonable defaults for these entries.<br />
<br />
<u><b>Jenkins Tutorials</b></u><br />
<br />
For a list of all jenkins related tutorials see <a href="https://codeandme.blogspot.com/p/jenkins-articles.html" target="_blank">Jenkins Tutorials Overview.</a><br />
<br />
<div style="font-family: "times new roman"; margin: 0px;">
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_06_advanced_configuration.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_06_advanced_configuration.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/06_advanced_configuration" target="_blank">browse the files</a> online.</div>
<br />
<b><u>Step 1: Advanced UI Section</u></b><br />
<br />
To simplify the job setup we now move all parameters except the build message to an advanced section.<br />
The only thing necessary in the <i>config.jelly</i> file is to create the section and move all affected input elements into it:<br />
<pre class="brush: xml"> <f:entry title="Custom build message" field="buildMessage">
<f:textbox default="${descriptor.getDefaultBuildMessage()}" />
</f:entry>
<f:advanced>
<f:entry title="Fail this build" field="failBuild">
<f:checkbox />
</f:entry>
<f:entry title="Build Delay" field="buildDelay">
<f:select />
</f:entry>
</f:advanced></pre>
<b><u><br /></u></b>
Afterwards the UI looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrU3IHsvAYH0G4Hc9Pf9zh35EPJaWy9x4mMUPqFhPdm5CI70TLdcYedm5wfCbV2Vm6dqOhJXG64ADqyNY7DxUrK1xXQcA9t6WiGlEGOhANjm32PgAt1j43OrmChBci_gjoVdgIw50yo6wJ/s1600/jenkins.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="138" data-original-width="595" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrU3IHsvAYH0G4Hc9Pf9zh35EPJaWy9x4mMUPqFhPdm5CI70TLdcYedm5wfCbV2Vm6dqOhJXG64ADqyNY7DxUrK1xXQcA9t6WiGlEGOhANjm32PgAt1j43OrmChBci_gjoVdgIw50yo6wJ/s1600/jenkins.png" /></a></div>
<br />
<b><u>Step 2: Java Refactoring</u></b><br />
<br />
Basically we do not need to change anything in the Java code to make this work. However we want to prepare a little for pipeline builds, so we remove non-required parameters from the constructor and create separate setters for them. To make Jenkins aware of these setters, use the <i>@DataBoundSetter</i> annotation:<br />
<pre class="brush: xml">public class HelloBuilder extends Builder implements SimpleBuildStep {
private boolean fFailBuild = false;
private String fBuildMessage;
private String fBuildDelay = "none";
@DataBoundConstructor
public HelloBuilder(String buildMessage) {
fBuildMessage = buildMessage;
}
@DataBoundSetter
public void setFailBuild(boolean failBuild) {
fFailBuild = failBuild;
}
@DataBoundSetter
public void setBuildDelay(String buildDelay) {
fBuildDelay = buildDelay;
}
}
</pre>
<div>
<br /></div>
<div>
Whenever a parameter is not required, remove it from the constructor and use a setter for it.</div>
Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-24370119454914906612018-12-03T15:59:00.003+01:002018-12-03T15:59:41.590+01:00Jenkins 5: Combo BoxesCombo boxes are the next UI element we will add to our builder.<br />
<br />
<u><b>Jenkins Tutorials</b></u><br />
<br />
For a list of all jenkins related tutorials see <a href="https://codeandme.blogspot.com/p/jenkins-articles.html" target="_blank">Jenkins Tutorials Overview.</a><br />
<br />
<div style="font-family: "times new roman"; margin: 0px;">
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_05_combo_input.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_05_combo_input.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/05_combo_input" target="_blank">browse the files</a> online.</div>
<br />
<b><u>Step 1: UI Definition</u></b><br />
<br />
In the <i>config.jelly</i> file we simply define that we want to use a combo box:<br />
<pre class="brush: xml"> <f:entry title="Build Delay" field="buildDelay">
<f:select />
</f:entry></pre>
The definition does not contain entries to select. These will be populated by the <i>Descriptor</i> class.<br />
<br />
<div>
<b><u>Step 2: Item Definition</u></b><br />
<br />
Jenkins will look for a method called <i>doFill<field>Items</i> in our <i>Descriptor</i> class to populate the combo. We are doing a first approach now to understand the scheme:<br />
<pre class="brush: xml"> public ListBoxModel doFillBuildDelayItems() {
ListBoxModel model = new ListBoxModel();
model.add(new Option("None", "none"));
model.add(new Option("Short", "short"));
model.add(new Option("Long", "long"));
return model;
}</pre>
<i>ListBoxModel</i> is basically an <i>ArrayList</i> of <i>Option</i> instances. The first string represents the text visible to the user, the second one the value that will actually be stored in our variable (see next step).<br />
<br />
If we would populate the combo this way, the first item would always be selected by default, even if we re-open a job that was configured differently. The <i>Option</i> constructor allows for a third parameter defining the selected state. We then just need to know the value that got stored with the job definition. Therefore we can inject the desired query parameter into our method parameters:<br />
<pre class="brush: xml"> public ListBoxModel doFillBuildDelayItems(@QueryParameter String buildDelay) {
ListBoxModel model = new ListBoxModel();
model.add(new Option("None", "none", "none".equals(buildDelay)));
model.add(new Option("Short", "short", "short".equals(buildDelay)));
model.add(new Option("Long", "long" , "long".equals(buildDelay)));
return model;
}
</pre>
Now <i>buildDelay</i> contains the value that got stored by the user when the build step was originally configured. By comparing its string representation we can set the right option in the combo.
Typically combo options could be populated from an Enum. To reduce the risk of typos we could write a small helper to create our <i>Options</i>:
<br />
<pre class="brush: xml"> public static Option createOption(Enum<?> enumOption, String jobOption) {
return new Option(enumOption.toString(), enumOption.name(), enumOption.name().equals(jobOption));
}
</pre>
<b><u><br /></u></b>
<b><u>Step 3: Glueing it all together</u></b><br />
<br />
Finally we need to extend our constructor with the new parameter. Then we can use it in our build step:<br />
<pre class="brush: xml">public class HelloBuilder extends Builder implements SimpleBuildStep {
private String fBuildDelay;
@DataBoundConstructor
public HelloBuilder(boolean failBuild, String buildMessage, String buildDelay) {
fBuildDelay = buildDelay;
}
@Override
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener)
throws InterruptedException, IOException {
listener.getLogger().println("This is the Hello plugin!");
listener.getLogger().println(getBuildMessage());
switch (getBuildDelay()) {
case "long":
Thread.sleep(10 * 1000);
break;
case "short":
Thread.sleep(3 * 1000);
break;
case "none":
// fall through
default:
// nothing to do
}
if (isFailBuild())
throw new AbortException("Build error forced by plugin settings");
}
public String getBuildDelay() {
return fBuildDelay;
}
}
</pre>
<div>
<br /></div>
</div>
Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-37164684090580113912018-12-03T14:54:00.003+01:002018-12-03T15:18:12.419+01:00Jenkins 4: Unit TestsNow that our builder plugin is working we should start writing some unit tests for it.<br />
<br />
<u><b>Jenkins Tutorials</b></u><br />
<br />
For a list of all jenkins related tutorials see <a href="https://codeandme.blogspot.com/p/jenkins-articles.html" target="_blank">Jenkins Tutorials Overview.</a><br />
<br />
<div style="font-family: "times new roman"; margin: 0px;">
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_04_unit_test.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_04_unit_test.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/04_unit_test" target="_blank">browse the files</a> online.</div>
<br />
<b><u>Step 1: Wrinting a simple test case</u></b><br />
<br />
Jenkins tests can be written as JUnit tests. The test instance needed for execution tests can be created using a <i>JUnit Rule</i>.<br />
<br />
Create a new <i>JUnit Test Case</i> <i>com.codeandme.jenkins.builder.HelloBuilderTes</i>t in the <i>src/test/java</i> folder:<br />
<pre class="brush: xml">public class HelloBuilderTest {
@Rule
public JenkinsRule fJenkinsInstance = new JenkinsRule();
@Test
public void successfulBuild() throws Exception {
HelloBuilder builder = new HelloBuilder(false, "JUnit test run");
FreeStyleProject job = fJenkinsInstance.createFreeStyleProject();
job.getBuildersList().add(builder);
FreeStyleBuild build = fJenkinsInstance.buildAndAssertSuccess(job);
fJenkinsInstance.assertLogContains("JUnit test run", build);
}
}</pre>
In line 4 we create a test instance for our unit test. This instance is used from line 10 onwards to create and run our test job. The instance provides a set of assertion commands which we use to check the build result and the log output of the job execution.<br />
<br />
You can run these tests as JUnit tests right from Eclipse or you can execute them via maven by running<br />
<pre class="brush: xml">mvn test</pre>
<br />
<b><u>Step 2: A test expecting an execution fail</u></b><br />
<br />
We use the same approach as before. To check for a failed build we need to run the build job a little bit different:<br />
<pre class="brush: xml"> @Test
public void failedBuild() throws Exception {
HelloBuilder builder = new HelloBuilder(true, "JUnit test fail");
FreeStyleProject job = fJenkinsInstance.createFreeStyleProject();
job.getBuildersList().add(builder);
QueueTaskFuture<FreeStyleBuild> buildResult = job.scheduleBuild2(0);
fJenkinsInstance.assertBuildStatus(Result.FAILURE, buildResult);
fJenkinsInstance.assertLogContains("JUnit test fail", buildResult.get());
}</pre>
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-6046712438359368782018-11-28T11:07:00.002+01:002018-11-28T11:07:21.393+01:00Jenkins 3: Text Input & ValidationOur builder UI is progressing: today we will add a text box with nice defaults and add input validation to it.<br />
<br />
<u><b>Jenkins Tutorials</b></u><br />
<br />
For a list of all jenkins related tutorials see <a href="https://codeandme.blogspot.com/p/jenkins-articles.html" target="_blank">Jenkins Tutorials Overview.</a><br />
<br />
<div style="font-family: "times new roman"; margin: 0px;">
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_03_text_input.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_03_text_input.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/03_text_input" target="_blank">browse the files</a> online.<br />
<br /></div>
<b><u>Step 1: The Text Box</u></b><br />
<br />
Adding a text box is as simple as adding the checkbox before. Add a new entry to the <i>config.jelly</i> file:<br />
<pre class="brush: xml"> <f:entry title="Custom build message" field="buildMessage">
<f:textbox />
</f:entry></pre>
Make sure you use a unique ID for <i>field</i>. Then add the new field to your builder by adding it to the constructor and create a getter for it:<br />
<pre class="brush: xml">public class HelloBuilder extends Builder implements SimpleBuildStep {
private String fBuildMessage;
@DataBoundConstructor
public HelloBuilder(boolean failBuild, String buildMessage) {
fFailBuild = failBuild;
fBuildMessage = buildMessage;
}
@Override
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener)
throws InterruptedException, IOException {
listener.getLogger().println(getBuildMessage());
if (isFailBuild())
throw new AbortException("Build error forced by plugin settings");
}
public String getBuildMessage() {
return fBuildMessage;
}
}</pre>
All done, give it a try!<br />
<br />
<b><u>Step 2: Default Value</u></b><br />
<br />
Some example data might help our users when filling out the buid parameters. Therefore lets provide a nice default value. This is done by adding a <i>default</i> attribute to the textbox:<br />
<pre class="brush: xml"><f:textbox default="${descriptor.getDefaultBuildMessage()}" /></pre>
We could have provided the default text directly in the attribute data. Instead we decided to fetch the default dynamically from the <i>Descriptor</i> class. No magic data binding here, so we need to implement the method:<br />
<pre class="brush: xml"> public static final class Descriptor extends BuildStepDescriptor<Builder> {
public String getDefaultBuildMessage() {
return "This is a great build";
}
}
</pre>
<div>
<br /></div>
<u><b>Step 3: Input Validation</b></u><br />
<br />
Having no build message would result in an empty log file, which is not what we want. With input validation we can force users to enter some text to the input box. Validation is done by providing a validator in the <i>Descriptor</i> class:<br />
<pre class="brush: xml"> public static final class Descriptor extends BuildStepDescriptor<Builder> {
public FormValidation doCheckBuildMessage(@QueryParameter String buildMessage) {
if (buildMessage.isEmpty())
return FormValidation.error("Please provide a build message.");
else if (buildMessage.trim().isEmpty())
return FormValidation.error("White space is not sufficient for a build message.");
else
return FormValidation.ok();
}
}</pre>
The method name needs to stick to the pattern <i>doCheck<Parameter></i>. Normally you would only provide the parameter in question to that method (again the parameter name needs to match your <i>field</i> ID) but if needed you could add parameters for other fields of the builder. This comes in handy when parameters depend on each other.Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com1tag:blogger.com,1999:blog-3460102793186140898.post-75814514981912575052018-11-28T10:14:00.001+01:002018-12-18T09:53:40.464+01:00Jenkins 2: A Builder Plugin & Some JellyIn the previous tutorial we did the basic setup for jenkins plugin development. Now we will try to create a plugin that actually runs a build step.<br />
<br />
<u><b>Jenkins Tutorials</b></u><br />
<br />
For a list of all jenkins related tutorials see <a href="https://codeandme.blogspot.com/p/jenkins-articles.html" target="_blank">Jenkins Tutorials Overview.</a><br />
<br />
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: "times new roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; margin: 0px; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;">
<div style="margin: 0px;">
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_02_builder_plugin.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_02_builder_plugin.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/02_builder_plugin" target="_blank">browse the files</a> online.</div>
</div>
<br />
<b><u>Step 1: The Basic Builder</u></b><br />
<br />
Maven allows to create a nice template for a builder plugin by calling<br />
<pre class="brush: xml">mvn archetype:generate -Dfilter=io.jenkins.archetypes:</pre>
then select the <i>hello-world-plugin</i>. But as we want to do it the hard way, we will add every single bit on our own and continue with the <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/01_empty_plugin" target="_blank">empty project</a> from our <a href="https://codeandme.blogspot.com/2018/11/jenkins-1-ide-setup-and-empty-plugin.html" target="_blank">previous tutorial</a>.<br />
<br />
The first thing we need is a class to implement our builder. Lets create a simple one. Create a new class <i>com.codeandme.jenkins.builder.HelloBuilder</i>:<br />
<pre class="brush: java">package com.codeandme.jenkins.builder;
import java.io.IOException;
public class HelloBuilder extends Builder implements SimpleBuildStep {
@DataBoundConstructor
public HelloBuilder() {
}
@Override
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener)
throws InterruptedException, IOException {
listener.getLogger().println("This is the Hello plugin!");
}
@Symbol("hello")
@Extension
public static final class Descriptor extends BuildStepDescriptor<Builder> {
@Override
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
return true;
}
@Override
public String getDisplayName() {
return "Code & Me - Hello World";
}
}
}
</pre>
<div>
<br /></div>
Jenkins expects the constructor to be augmented with the <i>@DataBoundConstructor</i> annotation. Later we will add our build parameters to it.<br />
<br />
The <i>perform()</i> method is the heart of our implementation. This is where we define what the build step should actually do. In this tutorial we focus on the definition, not the execution so we are just printing some log message to detect that our build step got triggered.<br />
<br />
Now lets put our focus on the <i>Descriptor</i> class. It actually describes what our plugin looks like, what parameter it uses and whether the user input is valid or not. You need to use a static class as a descriptor and augment it with the <i>@Extension</i> annotation to allow jenkins to detect it automatically.<br />
<br />
<i>isApplicable()</i> might be the most important one as it denotes if ou plugin is usable for the current project type.<br />
<br />
Start a jenkins test server using<br />
<pre class="brush: xml">mvn hpi:run -Djetty.port=8090</pre>
Create a new <i>Freestyle Project</i> and add your custom build step to it. Then execute the job and browse the log for our log message.<br />
<br />
<b><u>Build Considerations</u></b><br />
<br />
When writing a plugin it is vital to understand which parts of the plugin do get executed on the master and which ones on a slave machine. The java code we just wrote does get executed on the master only. Even when a job is executed on a slave machine, the perform() method still gets executed on master. Therefore we cannot use classic java libraries like NIO or the ProcessBuilder to access and execute stuff on the slave. Instead we need to use abstractions like the workspace and the launcher parameters we get from Jenkins. These objects will take care to delegate IO calls or program executions to the slave.<br />
<br />
Make sure you always keep focus which parts of your code get executed where.<br />
<br />
<b><u>Step 2: Basic UI</u></b><br />
<br />
Next we need a <i>*.jelly</i> file to describe how the UI should look like. Therefore create a new package in <i>src/main/resources</i> named <i>com.codeandme.jenkins.builder.HelloBuilder</i>. That is right, the package equals the class name of our builder class. Then create a <i>config.jelly</i> file inside that package:<br />
<br />
<pre class="brush: xml"><?jelly escape-by-default='true'?>
<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:block>
<h1>Code &amp; Me Productions</h1>
<p>We build the best <i>hellos</i> in the world</p>
</f:block>
</j:jelly>
</pre>
<div>
<br /></div>
Inside the jelly definition we can use plain HTML code. Run your test instance again to see your changes in your project configuration view.<br />
<br />
<b><u>Step 3: Checkbox Input</u></b><br />
<br />
Time to add some input. All build parameters need at least 2 steps of implementation: first we need to define the UI in the <i>config.jelly</i> file, then we need to define the parameters in the java class. Optionally we may add additional checks in the Descriptor class.<br />
<br />
To define the UI for the checkbox we add following code to our jelly file:<br />
<pre class="brush: xml"> <f:entry title="Fail this build" field="failBuild">
<f:checkbox />
</f:entry></pre>
This will create a label using the <i>title</i> field and a checkbox on the right side of the label. The <i>field</i> name is important as this is the ID of our field which we now use in the Java code:<br />
<pre class="brush: xml">public class HelloBuilder extends Builder implements SimpleBuildStep {
private boolean fFailBuild;
@DataBoundConstructor
public HelloBuilder(boolean failBuild) {
fFailBuild = failBuild;
}
@Override
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener)
throws InterruptedException, IOException {
listener.getLogger().println("This is the Hello plugin!");
if (isFailBuild())
throw new AbortException("Build error forced by plugin settings");
}
public boolean isFailBuild() {
return fFailBuild;
}
}
</pre>
<div>
<br /></div>
<div>
The new parameter needs to be added to our constructor. Make sure you use the same name as in the jelly file. Additionally we need a getter for our parameter. It will be queried to populate the UI when you configure your job and when the job gets executed. Jenkins expects the name of the getter to match the field name of your jelly file.<br />
<br />
During the build we evaluate our parameter and throw an <i>AbortException</i> in case our builder is expected to fail.<br />
<br />
<b><u>Step 4: Adding Help</u></b><br />
<br />
Lots of parameters in Jenkins plugins show a help button on the righthand side of the form. These buttons automatically appear when corresponding help files exist in the right location.<br />
<br />
A general help file for the builder named <i>help.html</i> needs to be placed next to the <i>config.jelly</i> file. You may add any arbitrary HTML content there, with no need to use <i><html></i> or <i><body></i> tags.<br />
<br />
To provide help for our checkbox we create another help file named <i>help-failBuild.html</i>. See the pattern? We again use the field ID and Jenkins figures out the rest.<br />
<br />
Instead of a plain HTML files we could also <a href="https://wiki.jenkins.io/display/JENKINS/Basic+guide+to+Jelly+usage+in+Jenkins#BasicguidetoJellyusageinJenkins-Helpfiles" target="_blank">provide jelly files</a> following the same name pattern.<br />
<br />
Changes like adding help or beautifying jelly files can be done without restarting our test instance. Simply change the file and reload the page in your webbrowser of your test instance.<br />
<br />
<b><u>Further reading</u></b><br />
<br />
Good documentation on writing forms seems to be rare on the internet. To me the best choice seems to find an existing plugin and browse the source code for reference. At least a <a href="https://reports.jenkins.io/core-taglib/jelly-taglib-ref.html#form" target="_blank">list of all control types</a> is available online.</div>
<div>
<br /></div>
<div>
<br /></div>
Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com2tag:blogger.com,1999:blog-3460102793186140898.post-51824333128399951362018-11-27T20:56:00.001+01:002018-12-11T16:09:30.713+01:00Jenkins 1: IDE Setup and an Empty PluginI recently started to write my first Jenkins plugin and I thought I share the experience with you. For that reason I started a new series of tutorials.<br />
<br />
Today we are building a simple Jenkins plugin using Eclipse. The basic steps of this tutorial are extracted from the <a href="https://wiki.jenkins.io/display/JENKINS/Plugin+tutorial" target="_blank">Jenkins Plugin Tutorial</a>.<br />
<br />
<u><b>Jenkins Tutorials</b></u><br />
<br />
For a list of all jenkins related tutorials see <a href="https://codeandme.blogspot.com/p/jenkins-articles.html" target="_blank">Jenkins Tutorials Overview.</a><br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_01_empty_plugin.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/jenkins_01_empty_plugin.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/jenkins/01_empty_plugin" target="_blank">browse the files</a> online.<br />
<b><u><br /></u></b>
<b><u>Prerequisites</u></b><br />
<br />
As you are interested in writing a plugin for <a href="https://jenkins.io/" target="_blank">Jenkins</a> I expect that you have a rough idea what Jenkins is used for and how to administer it.<br />
<br />
While our build environment allows to run a test instance of Jenkins with our plugin enabled I also liked to have a 'real' Jenkins instance available to test my plugins. Therefore I use <a href="https://www.docker.com/" target="_blank">docker</a> to quickly get started with a woking Jenkins installation.<br />
<br />
Once <a href="https://docs.docker.com/install/linux/docker-ce/debian/" target="_blank">you have installed docker</a> (<a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-debian-9" target="_blank">extended tutorial for debian</a>), you can download the latest Jenkins container by using<br />
<pre class="brush: shell">docker pull jenkins/jenkins:lts</pre>
<br />
Now run your container using<br />
<pre class="brush: shell">docker run -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts</pre>
<br />
After the startup process your server is reachable via <a href="http://localhost:8080/">http://localhost:8080/</a>.<br />
<br />
To manage containers use commands<br />
<pre class="brush: shell">docker container ls
docker container stop <container name>
</pre>
<br />
<b><u>Step 1: Maven configuration</u></b><br />
<br />
We will need <a href="https://maven.apache.org/" target="_blank">maven </a>installed and ready for creating the project, building and testing it, so make sure you have set it up correctly.<br />
<br />
Maven needs some configuration ready to learn about jenkins plugins, therefore you need to adapt the configuration file slightly. On linux change <i>~/.m2/settings.xml</i>, on windows modify/create <i>%USERPROFILE%\.m2\settings.xml</i> and set following content:<br />
<pre class="brush: xml"><settings>
<pluginGroups>
<pluginGroup>org.jenkins-ci.tools</pluginGroup>
</pluginGroups>
<profiles>
<!-- Give access to Jenkins plugins -->
<profile>
<id>jenkins</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<mirrors>
<mirror>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
<mirrorOf>m.g.o-public</mirrorOf>
</mirror>
</mirrors>
</settings></pre>
<br />
<b>Hint:</b> On windows I had to remove the settings file <i><maven install folder>\conf\settings.xml</i> as it was used in favor of my profile settings.<br />
<br />
<b><u>Step 2: Create the plugin skeleton</u></b><br />
<br />
To create the initial project open a shell and execute:<br />
<pre class="brush: xml">mvn archetype:generate -Dfilter=io.jenkins.archetypes:empty-plugin</pre>
You will be asked some questions how your plugin should be configurated:
<br />
<pre class="brush: xml">[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.0.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: remote -> io.jenkins.archetypes:empty-plugin (Skeleton of a Jenkins plugin with a POM and an empty source tree.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1
Choose io.jenkins.archetypes:empty-plugin version:
1: 1.0
2: 1.1
3: 1.2
4: 1.3
5: 1.4
Choose a number: 5:
[INFO] Using property: groupId = unused
Define value for property 'artifactId': builder.hello
Define value for property 'version' 1.0-SNAPSHOT: :
[INFO] Using property: package = unused
Confirm properties configuration:
groupId: unused
artifactId: builder.hello
version: 1.0-SNAPSHOT
package: unused
Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: empty-plugin:1.4
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: unused
[INFO] Parameter: artifactId, Value: builder.hello
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: unused
[INFO] Parameter: packageInPathFormat, Value: unused
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: unused
[INFO] Parameter: groupId, Value: unused
[INFO] Parameter: artifactId, Value: builder.hello
[INFO] Project created from Archetype in dir: ~/Eclipse/codeandme.blogspot.com/ws/builder.hello
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 36.202 s
[INFO] Finished at: 2018-11-27T20:34:15+01:00
[INFO] Final Memory: 16M/169M
[INFO] ------------------------------------------------------------------------
</pre>
<div>
<br /></div>
We just created the basic skeleton files and could start working right away. But as we want to do it the eclipse way we need to convert the project to a proper eclipse project. Therefore change into the created project directory and execute:<br />
<pre class="brush: xml">mvn -DdownloadSources=true -DdownloadJavadocs=true -DoutputDirectory=target/eclipse-classes eclipse:eclipse</pre>
<br />
The first run might take some time as maven has to fetch tons of dependencies. So sit back and enjoy the show...<br />
<br />
Once this step is done we can import our project using the Eclipse import wizard using <i>File / Import...</i> and then select<i> General/Existing Projects into Workspace</i>. On the following page select the project folder that was created by maven.<br />
<br />
<b><u>Step 3: Update configuration files</u></b><br />
<br />
The created <i>pom.xml</i> file for our plugin provides a good starting point for development. Typically you might want to update it a little before you actually start coding. Fields like <i>name</i>, <i>description</i>, <i>license </i>should be pretty clear. More interesting is<br />
<pre class="brush: xml"> <properties>
<jenkins.version>2.7.3</jenkins.version>
<java.level>7</java.level>
</properties></pre>
Upgrading the java level to 8 should be pretty safe these days. Further Jenkins 2.7.3 is really outdated. To check out which versions are available you may browse the <a href="https://repo.jenkins-ci.org/webapp/#/artifacts/browse/tree/General/releases/org/jenkins-ci/main/jenkins-war" target="_blank">jenkins artifactory server</a>. Open the <i>jenkins-war</i> node and search for a version you would like to use.<br />
<br />
I further adapt the <i>.project</i> file and remove the <i>groovyNature</i> as this would trigger install requests for groovy support in eclipse. As we are going to write java code, we do not need groovy.<br />
<br />
<b><u>Step 4: Build & Deploy the Plugin</u></b><br />
<br />
To build your plugin simply run<br />
<pre class="brush: xml">mvn package</pre>
This will build and test your package. Further it creates an installable *.hpi package in the <i>com.codeandme.jenkins.helloworld/target</i> folder.<br />
<br />
<b><u>Step 5: Test the plugin in a test instance</u></b><br />
<br />
To see your plugin in action you might want to execute it in a test instance of Jenkins. Maven will help us to set this up:<br />
<pre class="brush: xml">mvn hpi:run -Djetty.port=8090</pre>
After the boot phase, open up your browser and point to <a href="http://localhost:8090/jenkins">http://localhost:8090/jenkins</a> to access your test instance.<br />
<br />
Debugging is also quite simple, just add environment settings to setup your remote debugger. On Windows this would be:<br />
<pre class="brush: xml">set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n</pre>
On Linux use<br />
<pre class="brush: xml">export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"</pre>
Then you should be able to setup a <i>Remote Java Application</i> debug configuration in Eclipse.<br />
<br />
Writing a simple builder will be our next step, stay tuned for the next tutorial.Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com2tag:blogger.com,1999:blog-3460102793186140898.post-49552683437613149522017-12-14T12:03:00.001+01:002017-12-14T12:03:32.821+01:00Debugger 11: Watch expressionsNow that we have variables working, we might also want to include watch expressions to dynamically inspect code fragments.<br />
<br />
<u><b>Debug Framework Tutorials</b></u><br />
<br />
For a list of all debug related tutorials see <a href="http://codeandme.blogspot.co.at/p/blog-page.html" target="_blank">Debug Framework Tutorials Overview.</a><br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/debugger_11_watch_expressions.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/debugger_11_watch_expressions.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/debugger/11_watch_expressions" target="_blank">browse the files</a> online.<br />
<div>
<b><u><br /></u></b></div>
<div>
<b><u>Step 1: Provide the Watch Expression Delegate</u></b></div>
<div>
<br /></div>
<div>
Watch points are implemented via an extension point. So switch to your <i>plugin.xml</i> and add a new extension point for <i>org.eclipse.debug.core.watchExpressionDelegates</i>.</div>
<div>
The new delegate simply points to our <i>debugModel</i> identifier: <i>com.codeandme.debugModelPresentation.textinterpreter</i> and provides a class implementation:</div>
<pre class="brush: java">public class TextWatchExpressionDelegate implements IWatchExpressionDelegate {
@Override
public void evaluateExpression(String expression, IDebugElement context, IWatchExpressionListener listener) {
if (context instanceof TextStackFrame)
((TextStackFrame) context).getDebugTarget().fireModelEvent(new EvaluateExpressionRequest(expression, listener));
}
}</pre>
Delegates can decide on which context they may operate. For our interpreter we could evaluate expressions on <i>StackFrames</i>, <i>Threads</i> or the <i>Process</i>, but typically evaluations do take place on a dedicated <i>StackFrame</i>.<br />
<b><u><br /></u></b>
<b><u>Step 2: Evaluation</u></b><br />
<br />
Now we apply the usual pattern: send an event, let the debugger process it and send some event back to the debug target. Once the evaluation is done we then will inform the provided listener of the outcome of the evaluation.<br />
<pre class="brush: java">public class TextDebugTarget extends TextDebugElement implements IDebugTarget, IEventProcessor {
@Override
public void handleEvent(final IDebugEvent event) {
[...]
} else if (event instanceof EvaluateExpressionResult) {
IWatchExpressionListener listener = ((EvaluateExpressionResult) event).getOriginalRequest().getListener();
TextWatchExpressionResult result = new TextWatchExpressionResult((EvaluateExpressionResult)event, this);
listener.watchEvaluationFinished(result);
}
}</pre>
The <a href="https://github.com/Pontesegger/codeandme/blob/master/debugger/11_watch_expressions/com.codeandme.debugger.textinterpreter.debugger/src/com/codeandme/debugger/textinterpreter/debugger/model/TextWatchExpressionResult.java" target="_blank">TextWatchExpressionResult</a> uses a <i>TextValue</i> to represent the evaluation result. As before with variables we may support nested child variables within the value. In case the evaluation failed for some reason we may provide error messages which do get displayed in the <i>Expressions</i> view.Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-2720030612724520732017-12-14T11:13:00.000+01:002017-12-14T11:13:07.241+01:00Debugger 10: Editing variablesIn the previous tutorial we introduced variables support for our debugger. Now lets see how we can modify variables dynamically during a debug session.<br />
<br />
<u><b>Debug Framework Tutorials</b></u><br />
<br />
For a list of all debug related tutorials see <a href="http://codeandme.blogspot.co.at/p/blog-page.html" target="_blank">Debug Framework Tutorials Overview.</a><br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/debugger_10_variable_editing.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/debugger_10_variable_editing.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/debugger/10_variable_editing" target="_blank">browse the files</a> online.<br />
<div>
<b><u><br /></u></b></div>
<div>
<b><u>Step 1: Allowing for editing and trigger update</u></b></div>
<div>
<br /></div>
<div>
First variables need to support editing. Then the variables view will automatically provide a text input box on the value field once clicked by the user. This is also a limitation: editing variables requires the framework to interpret an input string and process it accordingly to the target language.</div>
<div>
<br /></div>
<div>
The relevant changes for the <a href="https://github.com/Pontesegger/codeandme/blob/master/debugger/10_variable_editing/com.codeandme.debugger.textinterpreter.debugger/src/com/codeandme/debugger/textinterpreter/debugger/model/TextVariable.java" target="_blank">TextVariable</a> class are shown below:</div>
<pre class="brush: java">public class TextVariable extends TextDebugElement implements IVariable {
@Override
public void setValue(String expression) {
getDebugTarget().fireModelEvent(new ChangeVariableRequest(getName(), expression));
}
@Override
public boolean supportsValueModification() {
return true;
}
@Override
public boolean verifyValue(String expression) throws DebugException {
return true;
}
}</pre>
<i>verifyValue(String)</i> and <i>setValue(String)</i> are used by the debug framework when a user tries to edit a variable in the UI. We do not need to update the value yet, but simply trigger an event to update the variable in the debugger.<br />
<br />
<b><u>Step 2: Variable update & refresh</u></b><br />
<br />
As our primitive interpreter accepts any kind of text variables there is nothing which can go wrong here. Instead of sending an update event for the changed variable we simply use the already existing <i>VariablesEvent</i> to force a refresh of all variables of the current <i>TextStackFrame</i>:<br />
<pre class="brush: java">public class TextDebugger implements IDebugger, IEventProcessor {
@Override
public void handleEvent(final IDebugEvent event) {
[...]
} else if (event instanceof ChangeVariableRequest) {
fInterpreter.getVariables().put(((ChangeVariableRequest) event).getName(), ((ChangeVariableRequest) event).getContent());
fireEvent(new VariablesEvent(fInterpreter.getVariables()));
}
}
}
</pre>
<div>
<br /></div>
Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-91155258003279236312017-07-14T10:31:00.001+02:002017-07-14T12:26:31.886+02:00EclipseCon Europe - Wouldn't it be cool to be on stage there?I remember my first EclipseCon and how impressed I was by the quality of talks and the technical skills those speakers showed. Proposing a talk of my own was totally out of question.<br />
A year later I thought: "proposing a talk does not harm, it will anyhow get rejected." So I spent 20 minutes on writing the proposal (took me 18 minutes to come up with a fancy title). Some months later I did my first talk at EclipseCon.<br />
<br />
EclipseCon does have a lot of technical talks that require deep knowledge of certain aspects of eclipse. However often the most interesting talks are not even technical. I remember a talk from last year: <a href="https://www.eclipsecon.org/europe2016/session/hearing-and-feeling-eclipse" target="_blank">Hearing and Feeling Eclipse</a> - a story about how blind people experience and use our beloved IDE. It is also interesting how companies use Eclipse technology, how to overcome typical obstacles or ideas and proposals on how to improve Eclipse.<br />
<br />
Such talks will attract developers and committers and introduce fresh ideas. Besides the audience is great, the spirit is awesome and it is perfect way to get in touch with people that work on similar topics.<br />
<br />
<a href="https://resheim.net/2017/07/eclipsecon-europe-2017-get-your-talk-proposals-in-now.html" target="_blank">Torkild wrote about the free tshirts</a> you get, but he forgot about the beer and the band (which hopefully will also play this year).<br />
<br />
So <a href="https://www.eclipsecon.org/europe2017/cfp" target="_blank">submit your talk right now</a>. Its not a contract yet you have to fulfill, its a proposal :)<br />
<br />
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-61363958865839332202017-05-15T18:59:00.002+02:002017-05-15T19:44:22.258+02:00Extract eclipse svg imagesWhen creating new icons for applications I like browsing <a href="http://git.eclipse.org/c/platform/eclipse.platform.images.git/tree/org.eclipse.images/eclipse-svg" target="_blank">existing eclipse svg images</a>. The repository structure is nice when you know what to look for. But with all its subfolders it is not suited for interactive browsing.<br />
<br />
While I am not worlds greatest bash script kiddie, I assembled a script that clones the repo and sorts its svg images. after execution you end up with a folder eclipse_images that hosts the svg files.<br />
<br />
If you improve the script, please post it here so others can benefit.<br />
<br />
<pre class="brush: xml">#!/bin/bash
# create working dir
mkdir eclipse_images
cd eclipse_images/
# get images
git clone git://git.eclipse.org/gitroot/platform/eclipse.platform.images.git
# extract all svg images
for line in `find eclipse.platform.images/ -iname "*.svg"`;
do
echo line | awk -v source="$line" '{str=source; gsub(/\//, "_", str); gsub(/eclipse.platform.images_org.eclipse.images_eclipse-svg_/, "", str); gsub(/icons_full_/, "", str); gsub(/_icons_/, "_", str); print "mv \"" source "\" \"" str "\""}' | bash -sx
done
# remove rest of repository
rm -rf eclipse.platform.images
# extract subtype 'wizard banner'
mkdir "wizban"
for line in `find . -maxdepth 1 -iname "*_wizban_*.svg"`;
do
mv "$line" "wizban"
done
# extract overlay images
mkdir "overlay"
for line in `find . -maxdepth 1 -regextype posix-extended -regex "^.*_ovr(16_.*)?.*.svg"`;
do
mv "$line" "overlay"
done
# extract progress indicators
mkdir "progress"
for line in `find . -maxdepth 1 -regextype posix-extended -regex "^.*_(prgss|progress)_.*.svg"`;
do
mv "$line" "progress"
done
# extract view images
mkdir "views"
for line in `find . -maxdepth 1 -regextype posix-extended -regex "^.*_e?view(16)?_.*.svg"`;
do
mv "$line" "views"
done
# ... and all the rest
declare -a arr=("obj16" "elcl16" "clcl16" "etool16" "ctool16" "obj")
mkdir "images"
for token in "${arr[@]}"
do
for line in `find . -maxdepth 1 -iname "*_${token}_*.svg"`;
do
mv "$line" "images"
done
done
cd ..
</pre>
Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com2tag:blogger.com,1999:blog-3460102793186140898.post-90700909874620119692017-04-24T12:01:00.000+02:002017-04-24T12:03:19.968+02:00Host your own eclipse signing serverWe handled <a href="http://codeandme.blogspot.co.at/2013/07/tycho-10-signing-plugins-and-executables.html" target="_blank">signing plugins with tycho</a> some time ago already. When working in a larger company you might want to keep your certificates and passphrases hidden from your developers. For such a scenario a signing server could come in handy.<br />
<br />
The <a href="https://wiki.eclipse.org/CBI" target="_blank">eclipse CBI project</a> provides such a server which just needs to get configured in the right way. Mikael Barbero <a href="https://dev.eclipse.org/mhonarc/lists/cbi-dev/msg01568.html" target="_blank">posted a short howto</a> on the mailing list, which should contain all you need. For a working setup example follow this tutorial.<br />
<br />
To have a test vehicle for signing we will reuse the <a href="http://codeandme.blogspot.co.at/2012/12/tycho-build-4-building-features.html" target="_blank">tycho 4 tutorial</a> source files.<br />
<br />
<u><b>Step 1: Get the service</b></u><br />
<br />
<a href="https://repo.eclipse.org/service/local/artifact/maven/redirect?r=cbi&g=org.eclipse.cbi&a=jar-signing-service&v=LATEST" target="_blank">Download the latest service snapshot file</a> and store it to a directory called <i>signingService</i>. Next <a href="https://repo.eclipse.org/service/local/artifact/maven/redirect?r=cbi&g=org.eclipse.cbi&a=jar-signing-service&c=tests&v=LATEST" target="_blank">download the test server</a>, we will use it to create a temporary certificate and keystore.<br />
<br />
Finally we need a template configuration file. <a href="http://git.eclipse.org/c/cbi/org.eclipse.cbi.git/plain/webservice/signing/jar/etc/jar-signing-service.properties" target="_blank">Download it</a> and store it to <i>signingService/jar-signing-service.properties</i>.<br />
<br />
<u><b>Step 2: A short test drive</b></u><br />
<br />
Open a console and change into the <i>signingService</i> folder. There execute:<br />
<pre class="brush: java">java -cp jar-signing-service-1.0.0-20170331.204711-10.jar:jar-signing-service-1.0.0-20170331.204711-10-tests.jar org.eclipse.cbi.webservice.signing.jar.TestServer</pre>
You should get some output giving you the local address of the signing service as long as the certificate store used:<br />
<pre class="brush: java">Starting test signing server at http://localhost:3138/jarsigner
Dummy certificates, temporary files and logs are stored in folder: /tmp/TestServer-2590700922068591564
Jarsigner executable is: /opt/oracle-jdk-bin-1.8.0.121/bin/jarsigner</pre>
We are not ready yet to sign code, but at least we can test if the server is running correctly. If you try to connect with a browser you should get a message that <i>HTTP method GET is not supported by this URL</i>.<br />
<br />
<u><b>Step 3: Preparing the tycho project</b></u><br />
<br />
We need some changes to our tycho project so it can make use of the signing server. Get the sources of the <a href="http://codeandme.blogspot.co.at/2012/12/tycho-build-4-building-features.html" target="_blank">tycho 4 tutorial</a> (checking out from git is fully sufficient) and add following code to <i>com.codeandme.tycho.releng/pom.xml</i>:<br />
<pre class="brush: xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<pluginRepositories>
<pluginRepository>
<id>cbi</id>
<url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<!-- enable jar signing -->
<plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
<version>${eclipse.jarsigner.version}</version>
<executions>
<execution>
<id>sign</id>
<goals>
<goal>sign</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
<configuration>
<signerUrl>http://localhost:3138/jarsigner</signerUrl>
</configuration>
</plugin>
</plugins>
</build>
</project></pre>
The code above shows purely additions to the <i>pom.xml</i>, no sections were removed or replaced.<br />
<br />
You may try to build your project with maven already. As I had problems to connect to https://timestamp.geotrust.com/tsa my local build failed, even if maven reported SUCCESS.<br />
<br />
<u><b>Step 4: Configuring a productive instance</b></u><br />
<br />
So lets get productive. Setting up your keystore with your certificates will not be handled by this tutorial, so I will reuse the keystore created by the test instance. Copy the <i>keystore.jks</i> file from the temp folder to the <i>signingService</i> folder. Then create a text file <i>keystore.pass</i>:<br />
<pre class="brush: java">echo keystorePassword >keystore.pass</pre>
<br />
Now we need to adapt the <i>jar-signing-service.properties</i> file to our needs:<br />
<pre class="brush: java">### Example configuration file
server.service.pathspec=/jarsigner
server.service.pathspec.versioned=false
jarsigner.bin=/opt/oracle-jdk-bin-1.8.0.121/bin/jarsigner
jarsigner.keystore=/somewhere/signingService/keystore.jks
jarsigner.keystore.password=/somewhere/signingService/keystore.pass
jarsigner.keystore.alias=acme.org
jarsigner.tsa=http://timestamp.entrust.net/TSS/JavaHttpTS
</pre>
<ul>
<li>By setting the versioned flag to <i>false</i> in line 4 we simplify the service web address (details can be found in the sample properties file).</li>
<li>Set the jarsigner executable path in line 6 according to your local environment.</li>
<li>Lines 8-10 contain details about the keystore and certificate to use, you will need to adapt them, but above settings should result in a working build.</li>
<li>The change in line 12 was necessary at the time of writing this tutorial because of connection problems to https://timestamp.geotrust.com/tsa.</li>
</ul>
Run your service using<br />
<pre class="brush: java">java -jar jar-signing-service-1.0.0-20170331.204711-10.jar</pre>
Remember that your productive instance now runs on port 8080, so adapt your <i>pom.xml</i> accordingly.Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com1tag:blogger.com,1999:blog-3460102793186140898.post-8072380255111578922017-03-15T13:02:00.001+01:002017-03-15T13:02:38.850+01:00Fancy tooltipsI always liked the tooltips available in eclipse editors. Having a browser widget that may capture focus is nice to display more complex help topics in the UI. Unfortunately the eclipse implementation is heavily bound to editors and cannot be used for other parts.<br />
<br />
Well, up to now. For EASE I wanted to reuse these tooltips to display API documentation in a treeviewer. The result looks quite satisfactory:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkh2g-4H1ed88mmP9uAI1BZ3upMLPY8GZrwGm4klm0_gNIMcqIXVLoKXyGYZH5gJ7Ns_kzYCZHq1GKVg9JXiTgxIEYSeXZ8baACAEGQGmzgdJMzfZ-HOuxMda-NX_DeF0m0AgH5Ww-IEjO/s1600/tooltips.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkh2g-4H1ed88mmP9uAI1BZ3upMLPY8GZrwGm4klm0_gNIMcqIXVLoKXyGYZH5gJ7Ns_kzYCZHq1GKVg9JXiTgxIEYSeXZ8baACAEGQGmzgdJMzfZ-HOuxMda-NX_DeF0m0AgH5Ww-IEjO/s400/tooltips.png" width="327" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
I built some API to add these tooltips to any kind of SWT controls. While it may not be perfect it seems rather simple to use for me.<br />
<pre class="brush: java"> final HoverManager hoverManager = new HoverManager(parent);
hoverManager.addHover(fModulesComposite.getTreeViewer(), new IHoverContentProvider() {
@Override
public void populateToolbar(BrowserInformationControl control, ToolBarManager toolBarManager) {
// nothing to do
}
@Override
public String getContent(Object origin, Object detail) {
return "<p>This is HTML content</p>";
}
});
</pre>
To see these tooltips in action <a href="https://www.eclipse.org/ease/download/" target="_blank">get a nightly build of EASE</a> and open the <i>Modules Explorer</i> view.<br />
<br />
Now I am wondering if there is any interest in making this API available for other eclipse projects. <br />
When extracting the functionality I had to access some internal classes from org.eclipse.jface.text and JDT - mostly because of package private methods. Porting back these changes would be possible, still I am wondering if org.eclipse.jface.text would be the right place for it. Why should a generic view depend on jface.text just to get nice tooltip support?<br />
<br />
So lets see if there is interest in adopting this feature and where to put it.Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com4tag:blogger.com,1999:blog-3460102793186140898.post-30997074628072050192016-12-15T08:05:00.002+01:002016-12-15T08:05:43.701+01:00Helpers: TableViewer Text ExportToday I would like to present a small helper class that exports the content of a TableViewer to a String representation.<br />
<br />
<u><b>How to use</b></u><br />
<br />
The <a href="https://github.com/Pontesegger/codeandme/blob/master/tools/com.codeandme.tools/src/com/codeandme/tools/ui/TableExporter.java" target="_blank">default implementation</a> creates a <a href="https://en.wikipedia.org/wiki/Comma-separated_values" target="_blank">CSV</a> output. Exporting the table is simple:<br />
<pre class="brush: java">TableExporter exporter = new TableExporter(tableViewer);
exporter.print(outputStream);
// exporter.toString() // serializes into a String
</pre>
The export uses sorting, filtering and column ordering so your output matches to what you see on the screen.<br />
<br />
<u><b>Other output formats </b></u><br />
<br />
If you are interested in other output formats, have a look at <a href="https://github.com/Pontesegger/codeandme/blob/master/tools/com.codeandme.tools/src/com/codeandme/tools/ui/HTMLTableExporter.java" target="_blank">HTMLTableExporter</a>, which renders the same data into an HTML table. You can easily extend the base class to adapt the output format to your own needs.<br />
<br />
<u><b>Examples</b></u><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh854xXnqNDTYYX8HgfvGMnktLQueJVmJyiEcb4CpiT4SjV0pA2pJinexnCXKSBz6XC8WSUap5dJdLB7p1aNi7WfHyqaqb6jPdMHFi9lnGRF7hD6YSlZ_2DemcyirjJb8pEC1v6SajRZgOS/s1600/table_export.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh854xXnqNDTYYX8HgfvGMnktLQueJVmJyiEcb4CpiT4SjV0pA2pJinexnCXKSBz6XC8WSUap5dJdLB7p1aNi7WfHyqaqb6jPdMHFi9lnGRF7hD6YSlZ_2DemcyirjJb8pEC1v6SajRZgOS/s1600/table_export.png" /></a></div>
<br />
<br />
rendered as CSV text:<br />
<br />
<pre class="brush: java">First,Last,City
Obi-Wan,Kenobi,Tatooine
Leia,Organa,Alderaan
Luke,Skywalker,Tatooine
Han,Solo,Corellia</pre>
<br />...or as HTML table<br />
<br />
<table>
<tbody>
<tr>
<th>First</th>
<th>Last</th>
<th>City</th>
</tr>
<tr>
<td>Obi-Wan</td>
<td>Kenobi</td>
<td>Tatooine</td>
</tr>
<tr>
<td>Leia</td>
<td>Organa</td>
<td>Alderaan</td>
</tr>
<tr>
<td>Luke</td>
<td>Skywalker</td>
<td>Tatooine</td>
</tr>
<tr>
<td>Han</td>
<td>Solo</td>
<td>Corellia</td>
</tr>
</tbody></table>
Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-52667726670050253282016-10-31T11:24:00.004+01:002016-10-31T11:24:40.026+01:00Oomph 05: Git checkoutIn this tutorial we will checkout a git repository and import its projects into the workspace.<br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/oomph_05_git_checkout.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/oomph_05_git_checkout.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/oomph/05_git_checkout" target="_blank">browse the files</a> online. <br />
<br />
For a list of all Oomph related tutorials see my <a href="http://codeandme.blogspot.co.at/p/oomph-articles.html" target="_blank">Oomph Tutorials Overview</a>.<br />
<u><b><br /></b></u>
<u><b>Step 1: Git clone task</b></u><br />
<br />
First open your <i>CodeAndMe.setup</i> file and look for a new child called <i>Git Clone</i>. Quite likely you will find it under <i>Additional Tasks</i>. All tasks available under that submenu are currently not installed. By selecting them eclipse triggers a background job to install the necessary components. Before you can use it a restart of your workbench will be required.<br />
<br />
Once you added a <i>Git Clone</i> task open its <i>Properties</i>. The bare minimum information to provide is the <i>Remote URI</i>. Set it to your clone location, eg. https://github.com/Pontesegger/codeandme.git<br />
<br />
We will also provide an <i>ID</i> so we can reference that setup task from other tasks later during the install. Therefore set <i>ID</i> to <i>codeandme.git.clone</i>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKsmV141G7s4wlMZWNpwierxeXbdWoeOy8w8DnNGOLvxXuN9gG11SLvGJOO9Xfd0lg1lfS3Ud1UPKhhPmCB_n1gAWXJeNYS2VUJvjDdwmRkYje50wHT9GLkI3GUSiwsxCArnwmJAf-B9eJ/s1600/oomph_05_01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKsmV141G7s4wlMZWNpwierxeXbdWoeOy8w8DnNGOLvxXuN9gG11SLvGJOO9Xfd0lg1lfS3Ud1UPKhhPmCB_n1gAWXJeNYS2VUJvjDdwmRkYje50wHT9GLkI3GUSiwsxCArnwmJAf-B9eJ/s1600/oomph_05_01.png" /></a></div>
The checkout <i>Location</i> is typically chosen by the user and should not be defined in the setup file. Users will set this during installation by using the <i>Git clone location rule</i> variable.<br />
<u><b><br /></b></u>
<u><b>Step 2: Import projects</b></u><br />
<br />
The clone task will create a local copy of the repository but that does not mean that its projects are already visible in the workspace.<br />
<br />
We need to add a dedicated <i>Projects Import</i> task. Further add a subnode of type <i>Source Locator</i> to it. Now switch to its <i>Properties</i> and set the <i>Root Folder</i>. Instead of providing a fixed location here we will refer to the Git Clone task we defined earlier.<br />
<br />
When a task has an ID, we can refer to it directly using a variable: ${task.id}. Further all task properties are available by just adding the property name: ${task.id.property_name}.<br />
<br />
To get the path where our git repository was checked out to we can use: ${codeandme.git.clone.location}<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuWfLXXt0Gs6ocUaa3hlrelZUyQwzTfhrIVj1UnJYMcB4NGNlFhiPlqBOP67DQRkohgdpR5qr0iB603v8Le0KbXPCLG7uC2mQrmWH8x6xqdU71CCbUWFGN7IK5iFXxoRCfOt2CSkxHDRwp/s1600/oomph_05_02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuWfLXXt0Gs6ocUaa3hlrelZUyQwzTfhrIVj1UnJYMcB4NGNlFhiPlqBOP67DQRkohgdpR5qr0iB603v8Le0KbXPCLG7uC2mQrmWH8x6xqdU71CCbUWFGN7IK5iFXxoRCfOt2CSkxHDRwp/s1600/oomph_05_02.png" /></a></div>
Give it a try to see that your task configuration is correct.<br />
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-65618437883573671562016-10-26T10:03:00.002+02:002016-10-26T10:03:16.841+02:00ECE 2016: Elevate your IDE with scriptsBeing at EclipseCon Europe for me is always a highlight throughout the year. While I am enjoying the talks and discussions (and some beer) you might be interested in what to expect from <a href="https://www.eclipsecon.org/europe2016/session/elevate-your-ide-scripts" target="_blank">my talk</a> on <a href="https://www.eclipse.org/ease/" target="_blank">EASE </a>today:<br />
<br />
Well I guess lots of you are already familiar with the basic concept of running scripts in Eclipse. Therefore the talk will focus on scripts that augment the IDE (or any other RCP application) in special ways. We will use them to add toolbar and menu items, we will create a custom builder prototype, learn something about the Event Broker and finally have a look at script deployment methods. Therefore we will use a generic keyword mechanism which can be extended easily to your own needs.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbNRv5Jb6c53jbqRCPX_fhjkeF7JH_DLYW6P1cvjSN6sVUrio8IXpdD_yPG4l8eSycQB2umfbWy_PdzGFwe22Egvzstn2t_EPlSKwgqeJ51gFNCpz1Wm8WAWIPOZkugOBXGLKY2MvZ3Dhp/s1600/explore2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbNRv5Jb6c53jbqRCPX_fhjkeF7JH_DLYW6P1cvjSN6sVUrio8IXpdD_yPG4l8eSycQB2umfbWy_PdzGFwe22Egvzstn2t_EPlSKwgqeJ51gFNCpz1Wm8WAWIPOZkugOBXGLKY2MvZ3Dhp/s1600/explore2.png" /></a></div>
<br />
Even if you do not care about scripting but prefer pure Java this talk might be for you. Using EASE you may extend the IDE without deployment of features (and without a restart).<br />
<br />
As I am a big fan of live sessions we will rush through some slides in 3 minutes and use the rest for a live demo. <br />
<br />
Got your interest? Join me on Wednesday, 11:45 at Silchersaal.Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com1tag:blogger.com,1999:blog-3460102793186140898.post-73828419417057935232016-09-21T11:14:00.003+02:002016-09-21T11:14:56.433+02:00Native browser for GTK on linuxHaving support for the internal browser is often not working out of the box on linux. You can check the status by opening your Preferences/General/Web Browser settings. If the radio button Use internal web browser is enabled (not necessarily activated) internal browser support is working, otherwise not.<br />
<br />
Most annoyingly without internal browser support help hovers in your text editors use a fallback mode not rendering links or images.<br />
<br />
To solve this issue you may first check the <a href="https://www.eclipse.org/swt/faq.php" target="_blank">SWT FAQ</a>. For me working on gentoo linux the following command fixed the problem:<br />
<pre class="brush: java">emerge net-libs/webkit-gtk:2</pre>
It is important to not only install the latest version of webkit-gtk which will not be recognized by Eclipse. After installation restart eclipse and your browser should work. Verified on Eclipse Neon.
Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0tag:blogger.com,1999:blog-3460102793186140898.post-33468361326668482382016-09-15T16:15:00.000+02:002016-09-15T16:15:01.941+02:00Oomph 04: P2 install tasksForm this tutorial onwards we are going to extend our project setup step by step. Today we are looking how to install additional plugins and features to our setup.<br />
<br />
Source code for this tutorial is available on github as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/oomph_04_p2_install_tasks.zip?raw=true">single zip archive</a>, as a <a href="https://github.com/Pontesegger/codeandme/blob/master/Code%20&%20Me%20Blog/oomph_04_p2_install_tasks.psf?raw=true">Team Project Set</a> or you can <a href="https://github.com/Pontesegger/codeandme/tree/master/oomph/04_p2_install_tasks" target="_blank">browse the files</a> online. <br />
<br />
For a list of all Oomph related tutorials see my <a href="http://codeandme.blogspot.co.at/p/oomph-articles.html" target="_blank">Oomph Tutorials Overview</a>.<br />
<br />
<u><b>Step 1: Add the Repository</b></u><br />
<br />
Open your Oomph setup file and create a new task of type <i>P2 Director</i>. To install components we need to provide a p2 site location and components from that location to install. So create a new <i>Repository</i> child to our task. When selected the <i>Properties</i> view will ask for a <i>URL</i>. Point to the p2 location you want to install stuff from. Leave <i>Type</i> to <i>Combined</i>. If you do not know about repository types you definitely do not need to change this setting.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbIqQCyRPMy8qhMIlMz0_R3dfpjgPzBGwsHwgbj7kT-u61fEiyIwLSavq84qBFdd-23Yjnpi3ma_LLdelnzDu9ee-5rXyskmQQNsSD0AWFcdLmz3IhVPFFuLkzIi_PYTI58X9T3lbcC7w1/s1600/oomph_01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="261" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbIqQCyRPMy8qhMIlMz0_R3dfpjgPzBGwsHwgbj7kT-u61fEiyIwLSavq84qBFdd-23Yjnpi3ma_LLdelnzDu9ee-5rXyskmQQNsSD0AWFcdLmz3IhVPFFuLkzIi_PYTI58X9T3lbcC7w1/s400/oomph_01.png" width="400" /></a></div>
When you are working with a p2 site provided by eclipse, Oomph can help to <a href="https://wiki.eclipse.org/Eclipse_Oomph_Authoring#How_to_find_a_P2_repository_at_Eclipse_using_the_Repository_Explorer" target="_blank">identify update site locations</a>.<br />
<br />
<u><b>Step 2: Add features</b></u><br />
<br />
Once you added the repository you can start to add features to be installed from that site. The manual way requires you to create a new child node of type <i>Requirement</i>. Go to its <i>Properties</i> and set <i>Name</i> to the feature id you want to install. You may add version ranges or make installs optional (which means do not throw an error when the feature cannot be installed).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxKDNsg0B5nOjJDAhHIkQpPkFRUtzIKSBmxgOAUZUJBP-frBgRknOBk71VAOdljkL-AM8Okp_9IDyreLOWeRFhmY1SQPBInupeMzEsdzyg-NvTZzHJ8IkS4NOwleRhH4f8mjkYIMPtJcj9/s1600/oomph_02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxKDNsg0B5nOjJDAhHIkQpPkFRUtzIKSBmxgOAUZUJBP-frBgRknOBk71VAOdljkL-AM8Okp_9IDyreLOWeRFhmY1SQPBInupeMzEsdzyg-NvTZzHJ8IkS4NOwleRhH4f8mjkYIMPtJcj9/s400/oomph_02.png" width="400" /></a></div>
The tricky part is to find out the name of the feature you want to install. I like to use the <a href="https://github.com/mbarbero/fr.obeo.releng.targetplatform" target="_blank">target platform editor</a> from Mickael Barbero with its nice code completion features. An even simpler way is to use the <i>Repository Explorer</i> from Oomph:<br />
<br />
Right click on your <i>Repository</i> node and select <i>Explore</i>. The <i>Repository Explorer</i> view comes up and displays features the same way as you might know it from the eclipse p2 installer. Now you can drag and drop entries to your P2 Director task.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1rEhJdAfC4N2rap87UmBVRvjo-cm2RSFIr7NlDPSnSuKWMhRwQDIVimh4bYowG5d9jMmHiYO1By89AHPf824v0x9A6nbQ2VMyhLJyilTF9R5_GmiqCb5I8XliatVTyijT-qi02T2zq8_k/s1600/oomph_03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="303" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1rEhJdAfC4N2rap87UmBVRvjo-cm2RSFIr7NlDPSnSuKWMhRwQDIVimh4bYowG5d9jMmHiYO1By89AHPf824v0x9A6nbQ2VMyhLJyilTF9R5_GmiqCb5I8XliatVTyijT-qi02T2zq8_k/s400/oomph_03.png" width="400" /></a></div>
<br />Christian Ponteseggerhttp://www.blogger.com/profile/13049318779927276910noreply@blogger.com0