ISIS-2450: minor fix to release docs

ISIS-2450: minor fix to release docs

= Cutting a Release

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
:page-partial:

The release process consists of:

* the release manager cutting the release (documented below)
* Members of the Apache Isis PMC xref:comguide:ROOT:verifying-releases.adoc[verifying] and voting on the release
* the release manager performing post-release tasks, for either a xref:comguide:ROOT:post-release-successful.adoc[successful] or an xref:comguide:ROOT:post-release-unsuccessful.adoc[unsuccessful] vote.

Apache Isis itself is released in one go, everything is ultimately a child of `isis-parent/pom.xml` (relative to the link:https://github.com/apache/isis[source code root]).
This section details the process for formally releasing this module.

The subsequent sections describe how other committers can xref:comguide:ROOT:verifying-releases.adoc[verify a release] and how the release manager can then perform xref:comguide:ROOT:post-release-successful.adoc[post-release] activities and set up for the next development iteration.

If you've not performed a release before, then note that there are some configuration xref:comguide:ROOT:release-process-prereqs.adoc[prerequisites] that must be configured first.
In particular, you'll need signed public/private keys, and the ASF Nexus staging repo inlocal `~/.m2/settings.xml` file.

These release notes using bash command line tools.
They should work on Linux and MacOS; for Windows, use mSysGit.

== Preparation

=== Obtain Consensus

Before releasing the framework, ensure there is consensus on the xref:docs:support:mailing-list.adoc[dev mailing list] that this is the right time for a release.
The discussion should include confirming the version number to be used, and to confirm content.

These discussions should also confirm the version number of the module being released.
This should be in line with our xref:comguide:ROOT:policies/versioning-policy.adoc#semantic-versioning[semantic versioning policy].

Make sure you have a JIRA ticket open against which to perform all commits.
In most cases a JIRA ticket will have been created at the beginning of the previous release cycle.

=== Pull down code to release

Set the HEAD of your local git repo to the commit to be released.
This will usually be the tip of the origin's `master` branch:

[source,bash,subs="attributes+"]
----
git checkout master
git pull --ff-only
----

=== License headers

The Apache Release Audit Tool `RAT` (from the http://creadur.apache.org[Apache Creadur] project) checks for missing license header files.
The parent `pom.xml` of each releasable module specifies the RAT Maven plugin, with a number of custom exclusions.

To run the RAT tool, use:

[source,bash,subs="attributes+"]
.find unapproved/missing licenses
----
mvn clean

pushd isis-parent
mvn org.apache.rat:apache-rat-plugin:check -D rat.numUnapprovedLicenses=1000 -Dreleased -P'!all'
popd

for a in `/bin/find . -name rat.txt -print`; do grep '!???' $a; done > /tmp/rat-qn.txt
for a in `/bin/find . -name rat.txt -print`; do grep '!AL' $a; done > /tmp/rat-al.txt
----

NOTE: the parent `pom.xml` in each of these locations has the `apache-rat-plugin` appropriately configured.

This script runs over all submodules, including non-released modules.

The command writes out a `target\rat.txt` for each submodule. missing license notes are indicated using the key `!???`.
The `for` command collates all the errors.

Investigate and fix any reported violations, typically by either:

* adding genuinely missing license headers from Java (or other) source files, or
* updating the `<excludes>` element for the `apache-rat-plugin` plugin to ignore test files, log files and any other non-source code files
* also look to remove any stale `<exclude>` entries

Once you've fixed all issues, run the script again to confirm that all license violations have been fixed.

=== Missing License Check

Although Apache Isis has no dependencies on artifacts with incompatible licenses, the POMs for some of these dependencies (in the Maven central repo) do not necessarily contain the required license information.
Without appropriate additional configuration, this would result in the generated `DEPENDENCIES` file and generated Maven site indicating dependencies as having "unknown" licenses.

Fortunately, Maven allows the missing information to be provided by configuring the `maven-remote-resources-plugin`.
This is stored in the `src/main/appended-resources/supplemental-models.xml` file, relative to the root of each releasable module.

It's first necessary to have built the framework locally at least once.

In the root directory:

[source,bash,subs="attributes+"]
----
mvn clean install -o
----

Once this is done, capture the missing license information using:

[source,bash,subs="attributes+"]
----
mvn license:download-licenses
groovy scripts/checkmissinglicenses.groovy
----

The Maven plugin creates a `license.xml` file in the `target/generated-resources` directory of each module.
The script then searches for these `licenses.xml` files, and compares them against the contents of the `supplemental-models.xml` file.

For example, the output could be something like:

[source,bash,subs="attributes+"]
----
licenses to add to supplemental-models.xml:

[org.slf4j, slf4j-api, 1.5.7]
[org.codehaus.groovy, groovy-all, 1.7.2]

licenses to remove from supplemental-models.xml (are spurious):

[org.slf4j, slf4j-api, 1.5.2]
----

If any missing entries are listed or are spurious, then update `supplemental-models.xml` and try again.

=== Update and preview website

Generate the website, ensuring that the config, examples, projdoc (system overview and global index) are all updated:

[source,bash,subs="attributes+"]
----
sh preview.sh
----

Check for any Asciidoc errors, and fix.

=== Commit changes

Commit any changes from the preceding steps:

[source,bash,subs="attributes+"]
----
git add ..
git commit -m "$ISISJIRA: updates to pom.xml etc for release"
----

== Releasing the Framework

=== Set environment variables

We use environment variables to parameterize as many of the steps as possible.
For example:

[source,bash,subs="attributes+"]
----
export ISISJIRA=ISIS-9999                           # <.>
export ISISTMP=/c/tmp                               # <.>
export ISISREL={page-isisrel}                             # <.>
export ISISRC=RC1                                   # <.>
export ISISBRANCH=release-$ISISREL-$ISISRC
export ISISART=isis
env | grep ISIS | sort
----
<.> set to an "umbrella" ticket for all release activities.
(One should exist already, xref:comguide:ROOT:post-release-successful.adoc#create-new-jira[created at] the beginning of the development cycle now completing).
<.> adjust by platform
<.> adjust as required
<.> adjust as necessary if this is not the first attempt to release

[IMPORTANT]
====
The branch name is intentionally *not* the same as the eventual tag names (eg `isis-{page-isisrel}`).
====

=== Create a release branch and worktree

The release is performed on a branch; if we are successful, this branch will be merged back into master.

We also recommend performing this work in a separate git worktree.
The original worktree can stay with the `master` branch and be used for documentation fixes, etc.

* create (but don't checkout) a release branch for the version number being released; eg:
+
[source,bash,subs="attributes+"]
----
git branch $ISISBRANCH
----

* Create a worktree for this branch:
+
[source,bash,subs="attributes+"]
----
git worktree add ../isis-release $ISISBRANCH
----

* Switch to the new worktree and push the branch:
+
[source,bash,subs="attributes+"]
----
cd ../isis-release
git push origin $ISISBRANCH -u
----

* Finally switch to the `isis-parent` directory:
+
[source,bash,subs="attributes+"]
----
cd isis-parent
----

=== Bump code to `$ISISREL`

Search through the `.java` files for `{page-isisprev}`, and change to `{page-isisrel}`.

=== Bump projects to `$ISISREL`

We use `mvn versions:set` to manually bump the release version:

[source,bash,subs="attributes+"]
----
mvn versions:set -DnewVersion=$ISISREL
mvn install -o -DskipTests

pushd ../starters
mvn versions:set -DnewVersion=$ISISREL
popd

git add ..
git commit -m "$ISISJIRA: bumps version to $ISISREL"
----

NOTE: After release, there is a xref:comguide:ROOT:cutting-a-release.adoc#reset-revision-property[similar step at the end] to reset back to `2.0.0-SNAPSHOT`.

=== Sanity check

Perform one last sanity check on the codebase.
Delete all Isis artifacts from your local Maven repo, then build using the `-o` offline flag:

[source,bash,subs="attributes+"]
----
rm -rf ~/.m2/repository/org/apache/isis
mvn clean install -o -Dreleased -P'!all'
----

[NOTE]
====
The `released` system property is set to only sanity check the modules actually to be released (ignores the incubator modules).

Partly that's because there's no need to check the incubator modules, it's also because the incubator modules require Java 11 (Java 8 is used for everything else)
====

=== Deploy

Since the `<version>` has already been updated, we just use `mvn deploy` to upload the artifacts.
We activate the (inherited) `apache-release` profile to bring in the `gpg` plugin for code signing.

The build creates a zip of the directory, so before executing the release we remove any other files.
Still in the `isis-parent` directory:

[source,bash]
----
pushd ..
git clean -dfx
popd
----

To deploy (upload the artifacts), we use:

[source,bash,subs="attributes+"]
----
mvn deploy \
    -Dapache-release \
    -Dgit
----

When prompted, enter your GPG passphrase.
(Or, it might be sufficient to add just `-Dgpg.passphrase="..."`)

IMPORTANT: This requires `gpg` v2.1 or later.

=== Tag the Release

Finally, tag the release:

[source,bash,subs="attributes+"]
----
git tag $ISISART-$ISISREL
git tag $ISISART-$ISISREL-$ISISRC
----

=== Check/Close Staging Repo

The `mvn deploy` commands will have uploaded all of the release artifacts into a newly created staging repository on the ASF Nexus repository server.

Log onto http://repository.apache.org[repository.apache.org] (using your ASF LDAP account):

image::release-process/nexus-staging-0.png[width="600px"]

And then check that the release has been staged (select `staging repositories` from left-hand side):

image::release-process/nexus-staging-1.png[width="600px"]

If nothing appears in a staging repo you should stop here and work out why.

Assuming that the repo has been populated, make a note of its repo id; this is needed for the voting thread.
In the screenshot above the id is `org.apache.isis-008`.

After checking that the staging repository contains the artifacts that you expect you should close the staging repository.
This will make it available so that people can check the release.

Press the Close button and complete the dialog:

image::release-process/nexus-staging-2.png[width="600px"]

Nexus should start the process of closing the repository.

image::release-process/nexus-staging-2a.png[width="600px"]

All being well, the close should (eventually) complete successfully (keep hitting refresh):

image::release-process/nexus-staging-3.png[width="600px"]

The Nexus repository manager will also email you with confirmation of a successful close.

If Nexus has problems with the key signature, however, then the close will be aborted:

image::release-process/nexus-staging-4.png[width="600px"]

Use `gpg --keyserver hkp://pgp.mit.edu --recv-keys nnnnnnnn` to confirm that the key is available.

[NOTE]
====
Unfortunately, Nexus does not seem to allow subkeys to be used for signing.
See xref:comguide:ROOT:key-generation.adoc[Key Generation] for more details.
====

[#reset-revision-property]
=== Reset `revision` property

At the beginning of the release process we bumped the version to the release version, ie `$ISISREL`.
With the release now deployed we now need to reset the revision back down to the base snapshot, ie `2.0.0-SNAPSHOT`.

[NOTE]
====
Previously we bumped to the next development snapshot.
However, this approach doesn't play well with CI/CD when Apache Isis is mounted as a git submodule, so instead we always use `2.0.0-SNAPSHOT` for all development work.
====

Therefore (still in the `isis-parent` directory):

[source,bash,subs="attributes+"]
----
mvn versions:set -DnewVersion=2.0.0-SNAPSHOT
mvn install -DskipTests -o -Dreleased -P'!all'

pushd ../starters
mvn versions:set -DnewVersion=2.0.0-SNAPSHOT
mvn install -DskipTests -o
popd

git add ..
git commit -m "$ISISJIRA: resetting version"
----

=== Push branch & tag

Push the release branch to origin:

[source,bash,subs="attributes+"]
----
git push -u origin $ISISBRANCH
----

and also push tag:

[source,bash,subs="attributes+"]
----
git push origin refs/tags/isis-$ISISREL:refs/tags/isis-$ISISREL-$ISISRC
git fetch
----

[NOTE]
====
The remote tags aren't visible locally but can be seen link:https://github.com/apache/isis/tags[online].
====

== Update starter apps

For each starter app, we create a new branch and make the changes there, pushing the branch back if the sanity check passes.

* for helloworld, the steps are:
+
[source,bash,subs="attributes+"]
----
git checkout master
git pull --ff-only

git checkout -b $ISISBRANCH

mvn versions:update-parent -DparentVersion=$ISISREL # <.>
mvn versions:set -DnewVersion=$ISISREL

mvn clean install -o
mvn spring-boot:run
----
<.> requires the current parent to exist locally in `~/.m2/repository`.
If this isn't the case, then manually edit instead.

* for simple app, the steps are almost the same:
+
[source,bash,subs="attributes+"]
----
git checkout master
git pull --ff-only

git checkout -b $ISISBRANCH

mvn versions:update-parent -DparentVersion=$ISISREL # <.>
mvn versions:set -DnewVersion=$ISISREL

mvn clean install -Dmetamodel.lockdown
mvn -pl webapp test -Dmavendeps.lockdown -B         # <.>
----
<.> requires the current parent to exist locally in `~/.m2/repository`.
If this isn't the case, then manually edit instead.
<.> the -B flag is required to avoid control characters in generated output
+
Approve any failed lockdown tests (the mavendeps will fail first time around because the dependencies on Apache Isis itself have just be bumped).
+
Repeat, then run the app as a sanity check:
+
[source,bash,subs="attributes+"]
----
mvn -pl webapp spring-boot:run
----
+
You could also check the output of the Cucumber tests, under:

** `webapp/target/cucumber-reports` and
** `webapp/target/cucumber-html-reports`.

* For both apps, commit any changes and then push the release branch to origin once ok:
+
[source,bash,subs="attributes+"]
----
git add .
git commit -m "$ISISJIRA - updates to $ISISREL"
git push -u origin $ISISBRANCH
----

== Preview website

We also prepare a preview of the next version of the website, then made accessible from link:https://isis.staged.apache.org[].

* Prerequisites:

** clone the link:https://github.com/apache/isis-site[] repo, alongside the `isis` repo:
+
[source,bash,subs="attributes+"]
----
git clone https://github.com/apache/isis-site ../isis-site
----

** in the `isis-site` repo, check out the `asf-staging` branch:
+
[source,bash,subs="attributes+"]
----
cd ../isis-site

git checkout asf-staging
git pull --ff-only
----

* still in the `isis-site` repo, delete all the files in `content/` _except_ for the `schema` and `versions` directories:
+
[source,bash,subs="attributes+"]
----
pushd content
for a in $(ls -1 | grep -v schema | grep -v versions)
do
    rm -rf $a
done
popd
----

* Back in the `isis` repo's worktree for `master` (as opposed to the `release` worktree, that is), generate the Antora site (from the top-level directory):
+
[source,bash,subs="attributes+"]
----
cd ../isis

sh preview.sh
----

* Copy the generated Antora site to `isis-site` repo's `contents` directory:
+
[source,bash,subs="attributes+"]
----
cp -Rf antora/target/site/* ../isis-site/content/.
----

* Back in the `isis-site` repo, commit the changes and preview:
+
[source,bash,subs="attributes+"]
----
cd ../isis-site

git add .
git commit -m "$ISISJIRA : staging changes to website"

sh preview.sh
----

* If happy, then push the changes:
+
[source,bash,subs="attributes+"]
----
git push origin asf-staging -u
----

Wait a minute or two; the site should be available at link:https://isis.staged.apache.org[]  (nb: 'staged', not 'staging').

== Voting

Once the artifacts have been uploaded, you can call a vote.

In all cases, votes last for 72 hours and require a +3 (binding) vote from members.

=== Start voting thread on dev mailing list

That is, link:mailto:dev@apache.isis.org[]

The following boilerplate is for a release of the Apache Isis Core.
Adapt as required:

Use the following subject, eg:

[source,subs="attributes+"]
----
[VOTE] Apache Isis Core release {page-isisrel} RC1
----

And use the following body:

[source,subs="attributes+"]
----
I've just cut a new release of the Apache Isis Framework.

The source code zip artifact has been uploaded to a staging repository on
https://repository.apache.org, along with its corresponding .asc signature.

In the source code repo the code has been tagged as isis-{page-isisrel}-RC1;
see https://github.com/apache/isis/tags

To verify the source code itself, you can use the following commands
(in an empty directory):

----------------------------------------------------------------
include::partial$verify-process.adoc[]
----------------------------------------------------------------

You can then test the helloworld or simpleapp starter apps, see:
https://isis.staged.apache.org/comguide/latest/verifying-releases.html.

You can also inspect the website in general, available at:
https://isis.staged.apache.org.

Please verify the release and cast your vote.
The vote will be open for a minimum of 72 hours.

[ ] +1
[ ]  0
[ ] -1
----

Remember to update:

* the version number (`{page-isisrel}` or whatever)
* the release candidate number (`RC1` or whatever)
* the `NEXUSREPONUM` to the repository id as provided by Nexus earlier (`11xx` or whatever)

Note that the email also references the procedure for other committers to xref:comguide:ROOT:verifying-releases.adoc[verify the release].

= Cutting a Release

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
:page-partial:

The release process consists of:

* the release manager cutting the release (documented below)
* Members of the Apache Isis PMC xref:comguide:ROOT:verifying-releases.adoc[verifying] and voting on the release
* the release manager performing post-release tasks, for either a xref:comguide:ROOT:post-release-successful.adoc[successful] or an xref:comguide:ROOT:post-release-unsuccessful.adoc[unsuccessful] vote.

Apache Isis itself is released in one go, everything is ultimately a child of `isis-parent/pom.xml` (relative to the link:https://github.com/apache/isis[source code root]).
This section details the process for formally releasing this module.

The subsequent sections describe how other committers can xref:comguide:ROOT:verifying-releases.adoc[verify a release] and how the release manager can then perform xref:comguide:ROOT:post-release-successful.adoc[post-release] activities and set up for the next development iteration.

If you've not performed a release before, then note that there are some configuration xref:comguide:ROOT:release-process-prereqs.adoc[prerequisites] that must be configured first.
In particular, you'll need signed public/private keys, and the ASF Nexus staging repo inlocal `~/.m2/settings.xml` file.

These release notes using bash command line tools.
They should work on Linux and MacOS; for Windows, use mSysGit.

== Preparation

=== Obtain Consensus

Before releasing the framework, ensure there is consensus on the xref:docs:support:mailing-list.adoc[dev mailing list] that this is the right time for a release.
The discussion should include confirming the version number to be used, and to confirm content.

These discussions should also confirm the version number of the module being released.
This should be in line with our xref:comguide:ROOT:policies/versioning-policy.adoc#semantic-versioning[semantic versioning policy].

Make sure you have a JIRA ticket open against which to perform all commits.
In most cases a JIRA ticket will have been created at the beginning of the previous release cycle.

=== Pull down code to release

Set the HEAD of your local git repo to the commit to be released.
This will usually be the tip of the origin's `master` branch:

[source,bash,subs="attributes+"]
----
git checkout master
git pull --ff-only
----

=== License headers

The Apache Release Audit Tool `RAT` (from the http://creadur.apache.org[Apache Creadur] project) checks for missing license header files.
The parent `pom.xml` of each releasable module specifies the RAT Maven plugin, with a number of custom exclusions.

To run the RAT tool, use:

[source,bash,subs="attributes+"]
.find unapproved/missing licenses
----
mvn clean

pushd isis-parent
mvn org.apache.rat:apache-rat-plugin:check -D rat.numUnapprovedLicenses=1000 -Dreleased -P'!all'
popd

for a in `/bin/find . -name rat.txt -print`; do grep '!???' $a; done > /tmp/rat-qn.txt
for a in `/bin/find . -name rat.txt -print`; do grep '!AL' $a; done > /tmp/rat-al.txt
----

NOTE: the parent `pom.xml` in each of these locations has the `apache-rat-plugin` appropriately configured.

This script runs over all submodules, including non-released modules.

The command writes out a `target\rat.txt` for each submodule. missing license notes are indicated using the key `!???`.
The `for` command collates all the errors.

Investigate and fix any reported violations, typically by either:

* adding genuinely missing license headers from Java (or other) source files, or
* updating the `<excludes>` element for the `apache-rat-plugin` plugin to ignore test files, log files and any other non-source code files
* also look to remove any stale `<exclude>` entries

Once you've fixed all issues, run the script again to confirm that all license violations have been fixed.

=== Missing License Check

Although Apache Isis has no dependencies on artifacts with incompatible licenses, the POMs for some of these dependencies (in the Maven central repo) do not necessarily contain the required license information.
Without appropriate additional configuration, this would result in the generated `DEPENDENCIES` file and generated Maven site indicating dependencies as having "unknown" licenses.

Fortunately, Maven allows the missing information to be provided by configuring the `maven-remote-resources-plugin`.
This is stored in the `src/main/appended-resources/supplemental-models.xml` file, relative to the root of each releasable module.

It's first necessary to have built the framework locally at least once:

[source,bash,subs="attributes+"]
----
mvn clean install -o
----

Once this is done, capture the missing license information using:

[source,bash,subs="attributes+"]
----
mvn license:download-licenses
groovy scripts/checkmissinglicenses.groovy
----

WARNING: this groovy script assumes it is being run from the `isis-parent` directory.

The Maven plugin creates a `license.xml` file in the `target/generated-resources` directory of each module.
The script then searches for these `licenses.xml` files, and compares them against the contents of the `supplemental-models.xml` file.

For example, the output could be something like:

[source,bash,subs="attributes+"]
----
licenses to add to supplemental-models.xml:

[org.slf4j, slf4j-api, 1.5.7]
[org.codehaus.groovy, groovy-all, 1.7.2]

licenses to remove from supplemental-models.xml (are spurious):

[org.slf4j, slf4j-api, 1.5.2]
----

If any missing entries are listed or are spurious, then update `supplemental-models.xml` and try again.

=== Update and preview website

Generate the website, ensuring that the config, examples, projdoc (system overview and global index) are all updated:

[source,bash,subs="attributes+"]
----
sh preview.sh
----

Check for any Asciidoc errors, and fix.

=== Commit changes

Commit any changes from the preceding steps:

[source,bash,subs="attributes+"]
----
git add ..
git commit -m "$ISISJIRA: updates to pom.xml etc for release"
----

== Releasing the Framework

=== Set environment variables

We use environment variables to parameterize as many of the steps as possible.
For example:

[source,bash,subs="attributes+"]
----
export ISISJIRA=ISIS-9999                           # <.>
export ISISTMP=/c/tmp                               # <.>
export ISISREL={page-isisrel}                             # <.>
export ISISRC=RC1                                   # <.>
export ISISBRANCH=release-$ISISREL-$ISISRC
export ISISART=isis
env | grep ISIS | sort
----
<.> set to an "umbrella" ticket for all release activities.
(One should exist already, xref:comguide:ROOT:post-release-successful.adoc#create-new-jira[created at] the beginning of the development cycle now completing).
<.> adjust by platform
<.> adjust as required
<.> adjust as necessary if this is not the first attempt to release

[IMPORTANT]
====
The branch name is intentionally *not* the same as the eventual tag names (eg `isis-{page-isisrel}`).
====

=== Create a release branch and worktree

The release is performed on a branch; if we are successful, this branch will be merged back into master.

We also recommend performing this work in a separate git worktree.
The original worktree can stay with the `master` branch and be used for documentation fixes, etc.

* create (but don't checkout) a release branch for the version number being released; eg:
+
[source,bash,subs="attributes+"]
----
git branch $ISISBRANCH
----

* Create a worktree for this branch:
+
[source,bash,subs="attributes+"]
----
git worktree add ../isis-release $ISISBRANCH
----

* Switch to the new worktree and push the branch:
+
[source,bash,subs="attributes+"]
----
cd ../isis-release
git push origin $ISISBRANCH -u
----

* Finally switch to the `isis-parent` directory:
+
[source,bash,subs="attributes+"]
----
cd isis-parent
----

=== Bump code to `$ISISREL`

Search through the `.java` files for `{page-isisprev}`, and change to `{page-isisrel}`.

=== Bump projects to `$ISISREL`

We use `mvn versions:set` to manually bump the release version:

[source,bash,subs="attributes+"]
----
mvn versions:set -DnewVersion=$ISISREL
mvn install -o -DskipTests

pushd ../starters
mvn versions:set -DnewVersion=$ISISREL
popd

git add ..
git commit -m "$ISISJIRA: bumps version to $ISISREL"
----

NOTE: After release, there is a xref:comguide:ROOT:cutting-a-release.adoc#reset-revision-property[similar step at the end] to reset back to `2.0.0-SNAPSHOT`.

=== Sanity check

Perform one last sanity check on the codebase.
Delete all Isis artifacts from your local Maven repo, then build using the `-o` offline flag:

[source,bash,subs="attributes+"]
----
rm -rf ~/.m2/repository/org/apache/isis
mvn clean install -o -Dreleased -P'!all'
----

[NOTE]
====
The `released` system property is set to only sanity check the modules actually to be released (ignores the incubator modules).

Partly that's because there's no need to check the incubator modules, it's also because the incubator modules require Java 11 (Java 8 is used for everything else)
====

=== Deploy

Since the `<version>` has already been updated, we just use `mvn deploy` to upload the artifacts.
We activate the (inherited) `apache-release` profile to bring in the `gpg` plugin for code signing.

The build creates a zip of the directory, so before executing the release we remove any other files.
Still in the `isis-parent` directory:

[source,bash]
----
pushd ..
git clean -dfx
popd
----

To deploy (upload the artifacts), we use:

[source,bash,subs="attributes+"]
----
mvn deploy \
    -Dapache-release \
    -Dgit
----

When prompted, enter your GPG passphrase.
(Or, it might be sufficient to add just `-Dgpg.passphrase="..."`)

IMPORTANT: This requires `gpg` v2.1 or later.

=== Tag the Release

Finally, tag the release:

[source,bash,subs="attributes+"]
----
git tag $ISISART-$ISISREL
git tag $ISISART-$ISISREL-$ISISRC
----

=== Check/Close Staging Repo

The `mvn deploy` commands will have uploaded all of the release artifacts into a newly created staging repository on the ASF Nexus repository server.

Log onto http://repository.apache.org[repository.apache.org] (using your ASF LDAP account):

image::release-process/nexus-staging-0.png[width="600px"]

And then check that the release has been staged (select `staging repositories` from left-hand side):

image::release-process/nexus-staging-1.png[width="600px"]

If nothing appears in a staging repo you should stop here and work out why.

Assuming that the repo has been populated, make a note of its repo id; this is needed for the voting thread.
In the screenshot above the id is `org.apache.isis-008`.

After checking that the staging repository contains the artifacts that you expect you should close the staging repository.
This will make it available so that people can check the release.

Press the Close button and complete the dialog:

image::release-process/nexus-staging-2.png[width="600px"]

Nexus should start the process of closing the repository.

image::release-process/nexus-staging-2a.png[width="600px"]

All being well, the close should (eventually) complete successfully (keep hitting refresh):

image::release-process/nexus-staging-3.png[width="600px"]

The Nexus repository manager will also email you with confirmation of a successful close.

If Nexus has problems with the key signature, however, then the close will be aborted:

image::release-process/nexus-staging-4.png[width="600px"]

Use `gpg --keyserver hkp://pgp.mit.edu --recv-keys nnnnnnnn` to confirm that the key is available.

[NOTE]
====
Unfortunately, Nexus does not seem to allow subkeys to be used for signing.
See xref:comguide:ROOT:key-generation.adoc[Key Generation] for more details.
====

[#reset-revision-property]
=== Reset `revision` property

At the beginning of the release process we bumped the version to the release version, ie `$ISISREL`.
With the release now deployed we now need to reset the revision back down to the base snapshot, ie `2.0.0-SNAPSHOT`.

[NOTE]
====
Previously we bumped to the next development snapshot.
However, this approach doesn't play well with CI/CD when Apache Isis is mounted as a git submodule, so instead we always use `2.0.0-SNAPSHOT` for all development work.
====

Therefore (still in the `isis-parent` directory):

[source,bash,subs="attributes+"]
----
mvn versions:set -DnewVersion=2.0.0-SNAPSHOT
mvn install -DskipTests -o -Dreleased -P'!all'

pushd ../starters
mvn versions:set -DnewVersion=2.0.0-SNAPSHOT
mvn install -DskipTests -o
popd

git add ..
git commit -m "$ISISJIRA: resetting version"
----

=== Push branch & tag

Push the release branch to origin:

[source,bash,subs="attributes+"]
----
git push -u origin $ISISBRANCH
----

and also push tag:

[source,bash,subs="attributes+"]
----
git push origin refs/tags/isis-$ISISREL:refs/tags/isis-$ISISREL-$ISISRC
git fetch
----

[NOTE]
====
The remote tags aren't visible locally but can be seen link:https://github.com/apache/isis/tags[online].
====

== Update starter apps

For each starter app, we create a new branch and make the changes there, pushing the branch back if the sanity check passes.

* for helloworld, the steps are:
+
[source,bash,subs="attributes+"]
----
git checkout master
git pull --ff-only

git checkout -b $ISISBRANCH

mvn versions:update-parent -DparentVersion=$ISISREL # <.>
mvn versions:set -DnewVersion=$ISISREL

mvn clean install -o
mvn spring-boot:run
----
<.> requires the current parent to exist locally in `~/.m2/repository`.
If this isn't the case, then manually edit instead.

* for simple app, the steps are almost the same:
+
[source,bash,subs="attributes+"]
----
git checkout master
git pull --ff-only

git checkout -b $ISISBRANCH

mvn versions:update-parent -DparentVersion=$ISISREL # <.>
mvn versions:set -DnewVersion=$ISISREL

mvn clean install -Dmetamodel.lockdown
mvn -pl webapp test -Dmavendeps.lockdown -B         # <.>
----
<.> requires the current parent to exist locally in `~/.m2/repository`.
If this isn't the case, then manually edit instead.
<.> the -B flag is required to avoid control characters in generated output
+
Approve any failed lockdown tests (the mavendeps will fail first time around because the dependencies on Apache Isis itself have just be bumped).
+
Repeat, then run the app as a sanity check:
+
[source,bash,subs="attributes+"]
----
mvn -pl webapp spring-boot:run
----
+
You could also check the output of the Cucumber tests, under:

** `webapp/target/cucumber-reports` and
** `webapp/target/cucumber-html-reports`.

* For both apps, commit any changes and then push the release branch to origin once ok:
+
[source,bash,subs="attributes+"]
----
git add .
git commit -m "$ISISJIRA - updates to $ISISREL"
git push -u origin $ISISBRANCH
----

== Preview website

We also prepare a preview of the next version of the website, then made accessible from link:https://isis.staged.apache.org[].

* Prerequisites:

** clone the link:https://github.com/apache/isis-site[] repo, alongside the `isis` repo:
+
[source,bash,subs="attributes+"]
----
git clone https://github.com/apache/isis-site ../isis-site
----

** in the `isis-site` repo, check out the `asf-staging` branch:
+
[source,bash,subs="attributes+"]
----
cd ../isis-site

git checkout asf-staging
git pull --ff-only
----

* still in the `isis-site` repo, delete all the files in `content/` _except_ for the `schema` and `versions` directories:
+
[source,bash,subs="attributes+"]
----
pushd content
for a in $(ls -1 | grep -v schema | grep -v versions)
do
    rm -rf $a
done
popd
----

* Back in the `isis` repo's worktree for `master` (as opposed to the `release` worktree, that is), generate the Antora site (from the top-level directory):
+
[source,bash,subs="attributes+"]
----
cd ../isis

sh preview.sh
----

* Copy the generated Antora site to `isis-site` repo's `contents` directory:
+
[source,bash,subs="attributes+"]
----
cp -Rf antora/target/site/* ../isis-site/content/.
----

* Back in the `isis-site` repo, commit the changes and preview:
+
[source,bash,subs="attributes+"]
----
cd ../isis-site

git add .
git commit -m "$ISISJIRA : staging changes to website"

sh preview.sh
----

* If happy, then push the changes:
+
[source,bash,subs="attributes+"]
----
git push origin asf-staging -u
----

Wait a minute or two; the site should be available at link:https://isis.staged.apache.org[]  (nb: 'staged', not 'staging').

== Voting

Once the artifacts have been uploaded, you can call a vote.

In all cases, votes last for 72 hours and require a +3 (binding) vote from members.

=== Start voting thread on dev mailing list

That is, link:mailto:dev@apache.isis.org[]

The following boilerplate is for a release of the Apache Isis Core.
Adapt as required:

Use the following subject, eg:

[source,subs="attributes+"]
----
[VOTE] Apache Isis Core release {page-isisrel} RC1
----

And use the following body:

[source,subs="attributes+"]
----
I've just cut a new release of the Apache Isis Framework.

The source code zip artifact has been uploaded to a staging repository on
https://repository.apache.org, along with its corresponding .asc signature.

In the source code repo the code has been tagged as isis-{page-isisrel}-RC1;
see https://github.com/apache/isis/tags

To verify the source code itself, you can use the following commands
(in an empty directory):

----------------------------------------------------------------
include::partial$verify-process.adoc[]
----------------------------------------------------------------

You can then test the helloworld or simpleapp starter apps, see:
https://isis.staged.apache.org/comguide/latest/verifying-releases.html.

You can also inspect the website in general, available at:
https://isis.staged.apache.org.

Please verify the release and cast your vote.
The vote will be open for a minimum of 72 hours.

[ ] +1
[ ]  0
[ ] -1
----

Remember to update:

* the version number (`{page-isisrel}` or whatever)
* the release candidate number (`RC1` or whatever)
* the `NEXUSREPONUM` to the repository id as provided by Nexus earlier (`11xx` or whatever)

Note that the email also references the procedure for other committers to xref:comguide:ROOT:verifying-releases.adoc[verify the release].


Delete 2018-07-30-Publishing-tens-of-millions-of-messages-per-second.adoc

= Publishing tens of millions of messages per second
Peter Lawrey
:hp-tags: Consulting, News Letter, Case Study

Welcome to our first newsletter from Chronicle giving you updates on developments at Chronicle Software plus tips and case studies around how Chronicle have helped clients to maximise value from their implementation.

=== Case Study
We have many examples where we have helped clients who are using our open source software to optimise their solution below is a recent example:-

We helped a client achieve a 12.5x throughput improvement for Chronicle Queue with minimal changes to their solution going from millions of messages per second to tens of millions of events per second.

A large Petrochemical company engaged us to help with performance issues they were seeing writing market data to our Chronicle Queue. The way they were using queue with an older version was limited to about 3.5 million messages per second (per queue). Rather than use more queues they needed to improve the throughput of each one.  After tuning their solution and using the last version of Chronicle Queue, they were able to achieve 8.5 million messages per second from Java.  What they really needed was a faster, low level way to write from C++ so we produced a bespoke solution for their use case which achieved 44 million messages per second.

NOTE: This was the throughput just to write the messages and didn’t include their logic.

=== Commercial Update
Due to ongoing success and growth within the Chronicle business, we have grown our Commercial team recruiting 2 experienced sales and business development professionals Rob Hunt and Andrew Twigg.  Rob has over 20 years experience within the financial markets, starting as a hedge fund trader he subsequently moved into software sales successfully selling to both buyside and sellside institutions. Andrew joins us from BGC where he was selling market data to financial institutions across Europe prior to that he worked in various senior sales roles within the financial markets at organisations including Thomson Reuters and Dow Jones.

Rob and Andrew in conjunction with Commercial Director Paul Hienkens are here to help you with any enquiries you may have around consulting or licensing our software solutions.

If you wish to discuss how our Chronicle Software experts can help you or wish to receive ongoing selected updates from Chronicle please contact us mailto:sales@chronicle-software.com[sales@chronicle-software.com]

Chronicle Software were founded in 2013 by Peter Lawrey the technical architect behind our software stack, as the company has grown Peter has assembled some of the best Java engineers and subject matter experts in the industry. The company’s heritage is firmly rooted in financial services, our business experts and developers are renowned for their proven track records


ISIS-3084: mignotes for 'Remove the Notion of mutable Collections'

= Migrating from M7 to M8

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
:page-partial:

(This page will be added to as development progresses).

== Programming Model

[cols="2a,3a", options="header"]

|===

| previously
| new

| For _Domain Objects_ we had support for (direct) _Collection_ modification (`addTo`, `removeFrom`).

As this is part of _Restful Object Specification 1.0_, it was implemented for the _RO Viewer_ and also the _WrapperFactory_.

Specifically REST endpoints (removed):
----
DomainObjectResource
  addToSet(...)
  addToList(...)
  removeFromCollection(...)
----
Events (removed)
----
CollectionAddToEvent
CollectionRemoveFromEvent
----
| Has been removed without replacement. (An updated _Restful Object Specification 2.0_ is in preparation.)

It has proven difficulty to define what an "execution" would mean for _Collection_ changes, hence we finally removed that notion.

In some situations it however  makes sense to limit which objects can be *added to* or *removed from* a _Collection_.
Thus, one would make the _Collection_ read only and then define _Actions_ that encapsulate the required business logic.

| `@DomainService.logicalTypeName` deprecated for removal
| use `@Named` or any other semantics supported by Spring
+
Behavioral change: `@DomainService.logicalTypeName` can now be left empty, which is interpreted as _unspecified/indifferent_

| `RecreatableDomainObject` interface removed
| use `ViewModel` interface instead

| `ViewModel.viewModelInit(String memento)` removed
| use a single (String) argument constructor instead; this will allow Java Records to be used as viewmodels (future work)

| `MetamodelEvent` as provided with _metamodel_ moved
| moved to _applib_; we introduced _interface_ `MetamodelListener` for convenience to maybe use instead
[source, java]
.Example
----
@Service
public class SeedService implements MetamodelListener {
/..
    @Override
    public void onMetamodelLoaded() {
        // seed database entities
    }
}
----

| Factory method `Identifier.propertyOrCollectionIdentifier(..)`
| was slit up into `Identifier.propertyIdentifier(..)` and `Identifier.collectionIdentifier(..)`

|===

== Commons

The following changed:
[cols="2a,3a", options="header"]

|===

| previously
| new

| `Result<T>` removed
| replaced by `Try<T>`; also promoting `Either<L, R>` to non-internal,
and introducing `Railway<F, S>`

|===

== Configuration

The following changed:
[cols="2a,3a", options="header"]

|===

| previously
| new

| (non-essential) object meta-data mixins were split out of `IsisModuleApplib`
| need to be explicitly imported via `IsisModuleApplibObjectMetadataMixins`

| default logging subscribers were split out of `IsisModuleApplib`
| need to be explicitly imported via `IsisModuleApplibChangeAndExecutionLoggers`

|===

== Other

=== Bill of Material / Parent POM

Folder moved `/isis-parent` -> `/bom`

[source, xml]
.Bill of Material
----
<!-- renamed
<groupId>org.apache.isis</groupId>
<artifactId>isis-parent</artifactId>
-->

<groupId>org.apache.isis</groupId>
<artifactId>isis-bom</artifactId>
----

=== Restclient

[source, java]
.Restclient Package Names
----
// renamed ...
//import org.apache.isis.extensions.restclient.RestfulClient;
//import org.apache.isis.extensions.restclient.RestfulClientConfig;
import org.apache.isis.viewer.restfulobjects.client.RestfulClient;
import org.apache.isis.viewer.restfulobjects.client.RestfulClientConfig;
----

[source, xml]
.Restclient Artifacts
----
<!-- renamed
<groupId>org.apache.isis.mappings</groupId>
<artifactId>isis-mappings-restclient-applib</artifactId>
-->

<groupId>org.apache.isis.viewer</groupId>
<artifactId>isis-viewer-restfulobjects-client</artifactId>
----

[source, java]
.Restclient Usage
----
// DigestResponse is no longer publicly visible, instead returning Try<T>
<T> Try<T> digest(Response response, Class<T> entityType);

// DigestResponse is no longer publicly visible, instead returning Try<Can<T>>
<T> Try<Can<T>> digestList(Response response, Class<T> entityType, GenericType<List<T>> genericType);
----

=== Mappings

[source, xml]
.Mappings
----
<!-- all removed
<groupId>org.apache.isis.mappings</groupId>
<artifactId>isis-mappings-...</artifactId>
-->
----

=== Mavendeps

[source, xml]
.integtests
----
<!-- removed
<groupId>org.apache.isis.mavendeps</groupId>
<artifactId>isis-mavendeps-integtests</artifactId>
<type>pom</type>
-->

<!-- instead use directly as required ... -->

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-integtestsupport-applib</artifactId>

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-fakedata-applib</artifactId>

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-fixtures-applib</artifactId>
----

[source, xml]
.unittests
----
<!-- removed
<groupId>org.apache.isis.mavendeps</groupId>
<artifactId>isis-mavendeps-unittests</artifactId>
<type>pom</type>
-->

<!-- instead use directly as required ... -->

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-unittestsupport-applib</artifactId>

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-fakedata-applib</artifactId>
----

= Migrating from M7 to M8

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
:page-partial:

(This page will be added to as development progresses).

== Programming Model

[cols="2a,3a", options="header"]

|===

| previously
| new

| `@DomainService.logicalTypeName` deprecated for removal
| use `@Named` or any other semantics supported by Spring
+
Behavioral change: `@DomainService.logicalTypeName` can now be left empty, which is interpreted as _unspecified/indifferent_

| `RecreatableDomainObject` interface removed
| use `ViewModel` interface instead

| `ViewModel.viewModelInit(String memento)` removed
| use a single (String) argument constructor instead; this will allow Java Records to be used as viewmodels (future work)

| `MetamodelEvent` as provided with _metamodel_ moved
| moved to _applib_; we introduced _interface_ `MetamodelListener` for convenience to maybe use instead
[source, java]
.Example
----
@Service
public class SeedService implements MetamodelListener {
/..
    @Override
    public void onMetamodelLoaded() {
        // seed database entities
    }
}
----

| Factory method `Identifier.propertyOrCollectionIdentifier(..)`
| was slit up into `Identifier.propertyIdentifier(..)` and `Identifier.collectionIdentifier(..)`

|===

== Commons

The following changed:
[cols="2a,3a", options="header"]

|===

| previously
| new

| `Result<T>` removed
| replaced by `Try<T>`; also promoting `Either<L, R>` to non-internal,
and introducing `Railway<F, S>`

|===

== Configuration

The following changed:
[cols="2a,3a", options="header"]

|===

| previously
| new

| (non-essential) object meta-data mixins were split out of `IsisModuleApplib`
| need to be explicitly imported via `IsisModuleApplibObjectMetadataMixins`

| default logging subscribers were split out of `IsisModuleApplib`
| need to be explicitly imported via `IsisModuleApplibChangeAndExecutionLoggers`

|===

== Other

=== Bill of Material / Parent POM

Folder moved `/isis-parent` -> `/bom`

[source, xml]
.Bill of Material
----
<!-- renamed
<groupId>org.apache.isis</groupId>
<artifactId>isis-parent</artifactId>
-->

<groupId>org.apache.isis</groupId>
<artifactId>isis-bom</artifactId>
----

=== Restclient

[source, java]
.Restclient Package Names
----
// renamed ...
//import org.apache.isis.extensions.restclient.RestfulClient;
//import org.apache.isis.extensions.restclient.RestfulClientConfig;
import org.apache.isis.viewer.restfulobjects.client.RestfulClient;
import org.apache.isis.viewer.restfulobjects.client.RestfulClientConfig;
----

[source, xml]
.Restclient Artifacts
----
<!-- renamed
<groupId>org.apache.isis.mappings</groupId>
<artifactId>isis-mappings-restclient-applib</artifactId>
-->

<groupId>org.apache.isis.viewer</groupId>
<artifactId>isis-viewer-restfulobjects-client</artifactId>
----

[source, java]
.Restclient Usage
----
// DigestResponse is no longer publicly visible, instead returning Try<T>
<T> Try<T> digest(Response response, Class<T> entityType);

// DigestResponse is no longer publicly visible, instead returning Try<Can<T>>
<T> Try<Can<T>> digestList(Response response, Class<T> entityType, GenericType<List<T>> genericType);
----

=== Mappings

[source, xml]
.Mappings
----
<!-- all removed
<groupId>org.apache.isis.mappings</groupId>
<artifactId>isis-mappings-...</artifactId>
-->
----

=== Mavendeps

[source, xml]
.integtests
----
<!-- removed
<groupId>org.apache.isis.mavendeps</groupId>
<artifactId>isis-mavendeps-integtests</artifactId>
<type>pom</type>
-->

<!-- instead use directly as required ... -->

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-integtestsupport-applib</artifactId>

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-fakedata-applib</artifactId>

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-fixtures-applib</artifactId>
----

[source, xml]
.unittests
----
<!-- removed
<groupId>org.apache.isis.mavendeps</groupId>
<artifactId>isis-mavendeps-unittests</artifactId>
<type>pom</type>
-->

<!-- instead use directly as required ... -->

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-unittestsupport-applib</artifactId>

<groupId>org.apache.isis.testing</groupId>
<artifactId>isis-testing-fakedata-applib</artifactId>
----


actualize Retrying failed tests docu

actualize Retrying failed tests docu

Sometimes it is required to retry a failed test. For the Junit tests, this can be achieved by setting the system property +test.retry.count+ to the maximum number of times you want the failed tests to be retried. All method tests will be executed until first successful run, but not more than +test.retry.count+ times.

For the Cucumber tests, there is an equivalent property +test.retry.count.cucumber+

Here is short example, for it we will use next test class:

[source,java]
----
include::{srcdir}/junit-retries/net/serenity/samples/retries/SampleTest.java[]
----

Steps class:

[source,java]
----
include::{srcdir}/junit-retries/net/serenity/samples/retries/TestSteps.java[]
----

If this test will be executed - it will fail:

[[retry_test_fail]]
.Report with failed scenario
image::retry_test_fail.png[]

If we set the property +test.retry.count=4+ in serenity.property file, the failing tests will be executed until they will be successful or the maximum number of tries will be reached:

The same test execution with the +test.retry.count+ property set will be successfull - because the method +then_example_result_should_be+ fails only twice, and third execution will be successful:

[[retry_test_fail]]
.Report with successful unstable scenario
image::retry_test_success_unstable.png[]

If you're using Jenkins for aggregating your test results use this folder pattern for JUnit test results:

----
target/site/serenity/SERENITY-JUNIT-*.xml
----

This will exclude the previous failed tests from your report.

Sometimes it is required to retry a failed test. This can be achieved by setting the system properties +junit.retry.tests+ to true and +max.retries+ to the number of times you want failed tests to be retried. If +max.retries+ provided and +junit.retry.tests+=true, all method tests will be executed until first successful run, but not more than 1 + +max.retries+ times.

Here is short example, for it we will use next test class:

[source,java]
----
include::{srcdir}/junit-retries/net/serenity/samples/retries/SampleTest.java[]
----

Steps class:

[source,java]
----
include::{srcdir}/junit-retries/net/serenity/samples/retries/TestSteps.java[]
----

If this test will be executed - it will fail:

[[retry_test_fail]]
.Report with failed scenario
image::retry_test_fail.png[]

If we provide next properties in serenity.property file, framework will execute tests more according to configuration until test will be successful or number of tries will be reached:

----
max.retries=4
junit.retry.tests=true
----

After executing same test it will be successful - because method +then_example_result_should_be+ fails only twice, and third "try" will successful:

[[retry_test_fail]]
.Report with successful scenario
image::retry_test_success.png[]

If you're using Jenkins for aggregating your test results use this folder pattern for JUnit test results:

----
target/site/serenity/SERENITY-JUNIT-*.xml
----

This will exclude the previous failed tests from your report.