faq tweak

faq tweak

= Frequently Asked Questions
Alex Miller
2015-01-01
:type: guides
:toc: macro
:icons: font

ifdef::env-github,env-browser[:outfilesuffix: .adoc]

toc::[]

These questions and answers are adapted from mailing lists and other Clojure community forums.

== Reader and Syntax

[[why_keywords]]
**What's the advantage of representing text tokens as keywords (instead of as strings)?**

Keywords are cached and interned. This means that a keyword is reused (reducing memory) everywhere in your program and that checks for equality really become checks for identity (which are fast). Additionally, keywords are invokable to look themselves up in a map and thus this enables the common pattern of extracting a particular field from a collection of maps possible.

[[reader_macros]]
**Why does Clojure not have user-extensible reader macros?**

The reader takes text (Clojure source) and returns Clojure data, which is subsequently compiled and evaluated. Reader macros tell the Clojure reader how to read something that is not a typical s-expression (examples are things like quoting `'` and anonymous functions `#()`). Reader macros can be used to define entirely new syntaxes read by the reader (for example: JSON, XML, or other formats) - this is a more powerful syntactic capability than regular macros (which come into play later at compile time).

However, unlike Lisp, Clojure does not allow the user to extend this set of reader macros. This avoids the possibility of creating code that another user cannot read (because they do not have the proper reader macros). Clojure gives back some of the power of reader macros with tagged literals, allowing you to create generically readable _data_, that is still extensible.

[[underscore]]
**What does an _ mean in a let binding or parameter?**

_ has no special meaning in Clojure as a symbol. However, it is a convention to use _ (or a leading _) to denote a binding that will not be used in the expression.

[source,clojure]
----
(defn get-x [point]
  (let [[x _] point]   ;; y-value of point is unused, so mark it with _
    x))
----

== Collections, Sequences, and Transducers

[[conj]]
**Why does `conj` add to the front of a list and the back of a vector?**

Most Clojure data structure operations, including `conj` (conjoin), are designed to give the user a performance expectation. With `conj`, the expectation is that insertion should happen at the place where this operation is efficient. Lists (as linked lists) can make a constant time insertion only at the front. Vectors (indexed) are designed to expand at the back. As the user, you should consider this when you choose which data structure to use. In Clojure, vectors are used with much greater frequency.

[[seqs_vs_colls]]
**I keep forgetting that after calling sequence functions on vectors/sets, the return value no longer is no longer has vector or set semantics.**

Generally you should divide the Clojure core functions into these two categories:

- Data structure functions - take a data structure and return a modified versions of that data structure (conj, disj, assoc, dissoc, etc). These functions always take the data structure _first_.
- Sequence functions - take a "seqable" and return a seqable. [Generally we try to avoid committing to the return values actually being an instance of ISeq - this allows for performance optimizations in some cases.] Examples are map, filter, remove, etc. All of these functions take the seqable _last_.

It sounds like you are using the latter but expecting the semantics of the former (which is a common issue for new Clojurists!). If you want to apply sequence functions but have more control over the output data structure, there are a number of ways to do that.

. Use data-structure equivalents like mapv or filterv, etc - this is a very limited set that lets you perform these ops but return a data structure rather than a seqable. `(mapv inc (filterv odd? [1 2 3]))`
. Pour the results of your sequence transformations back into a data structure with into: `(into [] (map inc (filter odd? [1 2 3])))`
. Use transducers (likely with `into`) - this has much the same effect as #2, but combinations of transformations can be applied more efficiently without creating any sequences - only the final result is built: `(into [] (comp (filter odd?) (map inc)) [1 2 3])`. As you work with larger sequences or more transformations, this makes a significant difference in performance.

Note that all of these are eager transformations - they produce the output vector when you invoke them. The original sequence version `(map inc (filter odd? [1 2 3]))` is lazy and will only produce values as needed (with chunking under the hood for greater performance). Neither of these is right or wrong, but they are both useful in different circumstances.

[[transducers_vs_seqs]]
**What are good use cases for transducers?**

When performing a series of transformations, sequences will create an intermediate (cached) sequence between each transformation. Transducers create a single compound transformation that is executed in one eager pass over the input. These are different models, which are both useful.

Performance benefits of transducers:

- Source collection iteration - when used on reducible inputs (collections and other things), avoid creating an unnecessary input collection sequence - helps memory and time.
- Intermediate sequences and cached values - as the transformation happens in a single pass, you remove all intermediate sequence and cached value creation - again, helps memory and time. The combination of the the prior item and this one will start to win big as the size of the input collection or number of transformations goes up (but for small numbers of either, chunked sequences can be surprisingly fast and will compete).

Design / usage benefits of transducers:

- Transformation composition - some use cases will have a cleaner design if they separate transformation composition from transformation application. Transducers support this.
- Eagerness - transducers are great for cases where eagerly processing a transformation (and potentially encountering any errors) is more important than laziness
- Resource control - because you have more control over when the input collection is traversed, you also know when processing is complete. It's thus easier to release or clean up input resources because you know when that happens.

Performance benefits of sequences:

- Laziness - if you will only need some of the outputs (for example a user is deciding how many to use), then lazy sequences can often be more efficient in deferring processing. In particular, sequences can be lazy with intermediate results, but transducers use a pull model that will eagerly produce all intermediate values.
- Infinite streams - because transducers are typically eagerly consumed, they don't match well with infinite streams of values

Design benefits of sequences:

- Consumer control - returning a seq from an API lets you combine input + transformation into something that gives the consumer control. Transducers don't work as well for this (but will work better for cases where input and transformation are separated).

== State and Concurrency

[[concurrency_features]]
**What are the trade-offs between reducers, core.async, and futures?**

Each of these really addresses a different use case.

- Reducers are best for fine-grained data parallelism when computing a transformation over existing in-memory data (in a map or vector). Generally it's best when you have thousands of small data items to compute over and many cores to do the work. Anything described as "embarrassingly parallel".
- futures are best for pushing work onto a background thread and picking it up later (or for doing I/O waits in parallel). It's better for big chunky tasks (go fetch a bunch of data in the background).
- core.async is primarily used to organize the subsystems or internal structure of your application. It has channels (queues) to convey values from one "subprocess" (go block) to another. So you're really getting concurrency and architectural benefits in how you break up your program. The killer feature you can really only get in core.async is the ability to wait on I/O events from multiple channels for the first response on any of them (via alt/alts). Promises can also be used to convey single values between independent threads/subprocesses but they are single delivery only.
- you didn't mention pmap, but tools like pmap, java.util queues and executors, and libraries like claypoole are doing coarse-level "task" concurrency. There is some overlap with core.async here which has a very useful transducer-friendly pipeline functionality.

[[write_skew]]
**Why does the Clojure STM does not guarantee serializability but only snapshot isolation?**

If reads were included by default, then STM would be slower (as more transactions would require serializability). However, in many cases, reads do not need to be included. Thus, users can choose to accept the performance penalty when it is necessary and get faster performance when it is not.

== Namespaces

[[ns_file]]
**Do namespaces map 1-to-1 with files?**

No (although that is typical). One namespace can be split across multiple files by using `load` to load secondary files and `in-ns` in those files to retain the namespace (clojure.core is defined in this way). Also, it is possible to declare multiple namespaces in a single file (although this is very unusual).

[[ns_as_fn]]
**Do namespaces work like regular functions? Looking at the syntax, it seems ns could be returning a function that makes a namespace, and then if you just stick parens around the contents of the file, that would be a regular S expression too. Does that imply you can put more than one in a file?**

ns is a macro that does a number of things:

- creates a new internal Namespace object (if it does not yet exist)
- makes that namespace the new current namespace (*ns*)
- auto-refers all vars from clojure.core and imports all classes from java.lang
- requires/refers other namespaces and vars as specified
- (and other optional things)

ns does not return a function or anything invokable as you suggest.

While ns is typically placed at the top of a clj file, it is actually just a normal macro and can be invoked at the repl just the same. It could also be used more than once in a single file (although this would be surprising to most clj programmers and would likely not work as desired in AOT).

== Compiler

[[direct_linking_repl]]
**How does direct linking affect the REPL experience?**

Anything that has been direct linked will not see redefinitions to vars. For example, if you redefine something in clojure.core, other parts of core that use that var will not see the redefinition (however anything that you newly compile at the REPL will). In practice, this is not typically a problem.

For parts of your own app, you may wish to only enable direct linking when you build and deploy for production, rather than using it when you developing at the REPL. Or you may need to mark parts of your app with ^:redef if you want to always allow redefinition or ^:dynamic for dynamic vars.

== Design and Use

[[encapsulation]]
**How do you achieve encapsulation with Clojure?**

Because of its focus on immutable data, there is generally not a high value placed on data encapsulation. Because data is immutable, there is no worry about someone else modifying a value. Likewise, because Clojure data is designed to be manipulated directly, there is significant value in providing direct access to data, rather than wrapping it in APIs.

All Clojure vars are globally available so again there is not much in the way of encapsulation of functions within namespaces. However, the ability to mark vars private (either using `defn-` for functions or `def` with `^:private` for values) is a convenience for a developer to indicate which parts of an API should be considered public for use vs part of the implementation.

= Frequently Asked Questions
Alex Miller
2015-01-01
:type: guides
:toc: macro
:icons: font

ifdef::env-github,env-browser[:outfilesuffix: .adoc]

toc::[]

These questions and answers are adapted from mailing lists and other Clojure community forums.

== Reader and Syntax

[[why_keywords]]
**What's the advantage of representing text tokens as keywords (instead of as strings)?**

Keywords are cached and interned. This means that a keyword is reused (reducing memory) everywhere in your program and that checks for equality really become checks for identity (which are fast).

[[reader_macros]]
**Why does Clojure not have user-extensible reader macros?**

The reader takes text (Clojure source) and returns Clojure data, which is subsequently compiled and evaluated. Reader macros tell the Clojure reader how to read something that is not a typical s-expression (examples are things like quoting `'` and anonymous functions `#()`). Reader macros can be used to define entirely new syntaxes read by the reader (for example: JSON, XML, or other formats) - this is a more powerful syntactic capability than regular macros (which come into play later at compile time).

However, unlike Lisp, Clojure does not allow the user to extend this set of reader macros. This avoids the possibility of creating code that another user cannot read (because they do not have the proper reader macros). Clojure gives back some of the power of reader macros with tagged literals, allowing you to create generically readable _data_, that is still extensible.

[[underscore]]
**What does an _ mean in a let binding or parameter?**

_ has no special meaning in Clojure as a symbol. However, it is a convention to use _ (or a leading _) to denote a binding that will not be used in the expression.

[source,clojure]
----
(defn get-x [point]
  (let [[x _] point]   ;; y-value of point is unused, so mark it with _
    x))
----

== Collections, Sequences, and Transducers

[[conj]]
**Why does `conj` add to the front of a list and the back of a vector?**

Most Clojure data structure operations, including `conj` (conjoin), are designed to give the user a performance expectation. With `conj`, the expectation is that insertion should happen at the place where this operation is efficient. Lists (as linked lists) can make a constant time insertion only at the front. Vectors (indexed) are designed to expand at the back. As the user, you should consider this when you choose which data structure to use. In Clojure, vectors are used with much greater frequency.

[[seqs_vs_colls]]
**I keep forgetting that after calling sequence functions on vectors/sets, the return value no longer is no longer has vector or set semantics.**

Generally you should divide the Clojure core functions into these two categories:

- Data structure functions - take a data structure and return a modified versions of that data structure (conj, disj, assoc, dissoc, etc). These functions always take the data structure _first_.
- Sequence functions - take a "seqable" and return a seqable. [Generally we try to avoid committing to the return values actually being an instance of ISeq - this allows for performance optimizations in some cases.] Examples are map, filter, remove, etc. All of these functions take the seqable _last_.

It sounds like you are using the latter but expecting the semantics of the former (which is a common issue for new Clojurists!). If you want to apply sequence functions but have more control over the output data structure, there are a number of ways to do that.

. Use data-structure equivalents like mapv or filterv, etc - this is a very limited set that lets you perform these ops but return a data structure rather than a seqable. `(mapv inc (filterv odd? [1 2 3]))`
. Pour the results of your sequence transformations back into a data structure with into: `(into [] (map inc (filter odd? [1 2 3])))`
. Use transducers (likely with `into`) - this has much the same effect as #2, but combinations of transformations can be applied more efficiently without creating any sequences - only the final result is built: `(into [] (comp (filter odd?) (map inc)) [1 2 3])`. As you work with larger sequences or more transformations, this makes a significant difference in performance.

Note that all of these are eager transformations - they produce the output vector when you invoke them. The original sequence version `(map inc (filter odd? [1 2 3]))` is lazy and will only produce values as needed (with chunking under the hood for greater performance). Neither of these is right or wrong, but they are both useful in different circumstances.

[[transducers_vs_seqs]]
**What are good use cases for transducers?**

When performing a series of transformations, sequences will create an intermediate (cached) sequence between each transformation. Transducers create a single compound transformation that is executed in one eager pass over the input. These are different models, which are both useful.

Performance benefits of transducers:

- Source collection iteration - when used on reducible inputs (collections and other things), avoid creating an unnecessary input collection sequence - helps memory and time.
- Intermediate sequences and cached values - as the transformation happens in a single pass, you remove all intermediate sequence and cached value creation - again, helps memory and time. The combination of the the prior item and this one will start to win big as the size of the input collection or number of transformations goes up (but for small numbers of either, chunked sequences can be surprisingly fast and will compete).

Design / usage benefits of transducers:

- Transformation composition - some use cases will have a cleaner design if they separate transformation composition from transformation application. Transducers support this.
- Eagerness - transducers are great for cases where eagerly processing a transformation (and potentially encountering any errors) is more important than laziness
- Resource control - because you have more control over when the input collection is traversed, you also know when processing is complete. It's thus easier to release or clean up input resources because you know when that happens.

Performance benefits of sequences:

- Laziness - if you will only need some of the outputs (for example a user is deciding how many to use), then lazy sequences can often be more efficient in deferring processing. In particular, sequences can be lazy with intermediate results, but transducers use a pull model that will eagerly produce all intermediate values.
- Infinite streams - because transducers are typically eagerly consumed, they don't match well with infinite streams of values

Design benefits of sequences:

- Consumer control - returning a seq from an API lets you combine input + transformation into something that gives the consumer control. Transducers don't work as well for this (but will work better for cases where input and transformation are separated).

== State and Concurrency

[[concurrency_features]]
**What are the trade-offs between reducers, core.async, and futures?**

Each of these really addresses a different use case.

- Reducers are best for fine-grained data parallelism when computing a transformation over existing in-memory data (in a map or vector). Generally it's best when you have thousands of small data items to compute over and many cores to do the work. Anything described as "embarrassingly parallel".
- futures are best for pushing work onto a background thread and picking it up later (or for doing I/O waits in parallel). It's better for big chunky tasks (go fetch a bunch of data in the background).
- core.async is primarily used to organize the subsystems or internal structure of your application. It has channels (queues) to convey values from one "subprocess" (go block) to another. So you're really getting concurrency and architectural benefits in how you break up your program. The killer feature you can really only get in core.async is the ability to wait on I/O events from multiple channels for the first response on any of them (via alt/alts). Promises can also be used to convey single values between independent threads/subprocesses but they are single delivery only.
- you didn't mention pmap, but tools like pmap, java.util queues and executors, and libraries like claypoole are doing coarse-level "task" concurrency. There is some overlap with core.async here which has a very useful transducer-friendly pipeline functionality.

[[write_skew]]
**Why does the Clojure STM does not guarantee serializability but only snapshot isolation?**

If reads were included by default, then STM would be slower (as more transactions would require serializability). However, in many cases, reads do not need to be included. Thus, users can choose to accept the performance penalty when it is necessary and get faster performance when it is not.

== Namespaces

[[ns_file]]
**Do namespaces map 1-to-1 with files?**

No (although that is typical). One namespace can be split across multiple files by using `load` to load secondary files and `in-ns` in those files to retain the namespace (clojure.core is defined in this way). Also, it is possible to declare multiple namespaces in a single file (although this is very unusual).

[[ns_as_fn]]
**Do namespaces work like regular functions? Looking at the syntax, it seems ns could be returning a function that makes a namespace, and then if you just stick parens around the contents of the file, that would be a regular S expression too. Does that imply you can put more than one in a file?**

ns is a macro that does a number of things:

- creates a new internal Namespace object (if it does not yet exist)
- makes that namespace the new current namespace (*ns*)
- auto-refers all vars from clojure.core and imports all classes from java.lang
- requires/refers other namespaces and vars as specified
- (and other optional things)

ns does not return a function or anything invokable as you suggest.

While ns is typically placed at the top of a clj file, it is actually just a normal macro and can be invoked at the repl just the same. It could also be used more than once in a single file (although this would be surprising to most clj programmers and would likely not work as desired in AOT).

== Compiler

[[direct_linking_repl]]
**How does direct linking affect the REPL experience?**

Anything that has been direct linked will not see redefinitions to vars. For example, if you redefine something in clojure.core, other parts of core that use that var will not see the redefinition (however anything that you newly compile at the REPL will). In practice, this is not typically a problem.

For parts of your own app, you may wish to only enable direct linking when you build and deploy for production, rather than using it when you developing at the REPL. Or you may need to mark parts of your app with ^:redef if you want to always allow redefinition or ^:dynamic for dynamic vars.

== Design and Use

[[encapsulation]]
**How do you achieve encapsulation with Clojure?**

Because of its focus on immutable data, there is generally not a high value placed on data encapsulation. Because data is immutable, there is no worry about someone else modifying a value. Likewise, because Clojure data is designed to be manipulated directly, there is significant value in providing direct access to data, rather than wrapping it in APIs.

All Clojure vars are globally available so again there is not much in the way of encapsulation of functions within namespaces. However, the ability to mark vars private (either using `defn-` for functions or `def` with `^:private` for values) is a convenience for a developer to indicate which parts of an API should be considered public for use vs part of the implementation.


Update free_skymaps.adoc

Update free_skymaps.adoc

Separated out angular maps browsing into own heading.

= How to create free SkyMaps
:author: Robert Allen
:revnumber:
:revdate: 2017/04/4 10:23
:relfileprefix: ../../
:imagesdir: ../..
:experimental:
ifdef::env-github,env-browser[:outfilesuffix: .adoc]

There are a plethora of ways to create skymaps with varying levels of difficulty and expense. The link:http://planetside.co.uk/[Terragen] program has been mentioned as one way to do so but it is now limited in its use and for commercial purposes is not free. Another program, link:https://www.daz3d.com/bryce-7-pro[Bryce], is also not free and seems to of stalled in development based off there only being a 32bit version available. Maybe they are doing things behind the scenes but its still not a free program. This articles intent is to give the JME3 user other options for the creation of skymaps using the free programs Blender and Gimp. It is not the be all, end all of skymap creation. If you know of better methods or tools please feel free to share your wisdom on the link:https://hub.jmonkeyengine.org/[forums].

== Blender

Using Blender you can create your skymaps for free. Once link:https://www.blender.org/[Blender] is setup, your skymaps creation can be done easily. For this article I will go into detail on how to setup Blender to create Angular maps based off a video produced by jMonkey contributor glaucomardano and mentioned in this link:https://hub.jmonkeyengine.org/t/jmonkeyengine-tutorial-how-to-create-skymaps-using-blender/19313[forum post]. Contributor glaucomardano did a good job on the video but it requires previous knowledge of Blender to be able to easily follow along. As such, I will translate it to paper for you. You can view his video by following this link: link:https://youtu.be/z38Aikz5nE8[jMonkeyEngine Tutorial - How to create skymaps using blender].

[NOTE]
====
This tutorial is an adaptation of contributor glaucomardanos production, not an exact duplicate. +
These steps assume you're using the default blender setup and apply to vers 2.78c+. +
This is in checklist format so you can mark items as you go to keep your place.
====

[%interactive]
.Blender Skybox Checklist
- [ ] Start with a new file in blender.
- [ ] Select the lamp and the default cube in the 3d view or in the Outliner panel (top-right panel) and delete them: kbd:[X]. They are not needed for this procedure.
- [ ] Select the camera object in the 3d view or in the Outliner panel then clear its location and rotation by using keyboard shortcuts kbd:[Alt]+kbd:[G] and kbd:[Alt]+kbd:[R].
- [ ] In the properties panel (lower-right panel) select the Object tab (orange box). This will give you a visual perspective of the camera changes you are about to make.
- [ ] Rename the Camera to Camera-north.
- [ ] Change your 3d view to top-ortho by pressing kbd:[NumPad 7] followed by kbd:[NumPad 5].
- [ ] With the camera still selected, press kbd:[R] immediately followed by kbd:[X] immediately followed by 90 immediately followed by kbd:[Enter] to rotate the camera 90 degrees around the X axis.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] Press kbd:[R] immediately followed by 180 immediately followed by kbd:[Enter]. This will rotate the camera 180 degrees around the Z axis. Rename this camera to Camera-south.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by 90 immediately followed by kbd:[Enter]. This will rotate the camera another 90 degrees around the Z axis. Rename this camera to Camera-west.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by -180 immediately followed by kbd:[Enter]. This will rotate the camera -180 degrees around the Z axis. Rename this camera to Camera-east.
- [ ] With your mouse inside the 3d view change your view to right-ortho by entering kbd:[NumPad 3].
- [ ] Select Camera-south in 3d view or in the Outliner panel (top-right panel) and press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by 90 immediately followed by kbd:[Enter]. This will rotate the camera 90 degrees around the X axis. The camera should now be pointing up. Rename this camera to Camera-up.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by 180 immediately followed by kbd:[Enter]. This will rotate the camera 180 degrees along the X axis. The camera should now be facing down. Rename this camera to Camera-down.
- [ ] Save your file.

[%interactive]
.Angular Map
- [ ] Open up a web browser and search for "free high res skymap" and select a Angular map of your choice or you can find some here at link:https://blenderartists.org/forum/showthread.php?24038-Free-high-res-skymaps-%2528Massive-07-update!%2529[https://blenderartists.org/forum/showthread.php?24038-Free-high-res-skymaps-%2528Massive-07-update!%2529] instead. Remember to select an *ANGULAR* map.
- [ ] Save a map to your pc in a place you can easily locate it from later in blender.

[%interactive]
.Textures Tab
- [ ] From the properties panel (bottom-right panel) select the Textures tab (red-white checkerboard) then press the new button to create a new texture. Rename this texture to AngMap.
- [ ] Under the Image panel select open and navigate to the file you saved earlier.
- [ ] In the Mapping panel select AngMap from the drop down box.
- [ ] In the Influence panel de-select blend (ble) and select horizon (hor).

[%interactive]
.Data Tab
- [ ] From the properties panel select the Data tab (reel to reel camera).
- [ ] In the lens panel change the Focal Lens value from 35 to 16. Do this for every camera.

[%interactive]
.Render Tab
- [ ] From the properties panel select the Render tab (normal looking camera).
- [ ] In the Dimensions panel set the resolution to any number that is a power of 2. For this example 1024 x 1024.
- [ ] Slide the resolution scale to 100%.
- [ ] In the Output panel change the image type to JPEG. I have found out by trial and error that using a JPEG file has the same image quality as a PNG or DDS file but with a huge difference in image size. A single PNG image will clock in at over 8mb to the JPEG size of 325kb. Even converting to a DDS file comes in at over 3mb for comparison (using RGB888 as is recommended by Momoko_Fan/Core Developer in this link:https://hub.jmonkeyengine.org/t/best-dds-format-for-skyfactory/17668/2[forum post]) with no gain in image quality that I could see. Your welcome to experiment on your own if you wish.
- [ ] Set the image format to RGB.
- [ ] Setting the quality slider to 0 has the effect of reducing the image size. In this example it reduced the image sizes to less than 125kb, once again with no discernable image degradation that I could see.

[%interactive]
.World Tab
- [ ] In the properties panel select the World tab.
- [ ] Check the Real Sky toggle.

[%interactive]
.Map Generator Setup
- [ ] From the header at the top of the 3d view click the btn:[Choose Screen Layout] button next to the word `Default` and select `Split Verticle F/R`.
- [ ] Click the `+` sign to create a new layout.
- [ ] Rename this new layout Angular Map Generator or a name of your choosing.
- [ ] In the left side 3d view, at the bottom, next to the word view, is the btn:[Current Editor Type] button. Click it and change it to `UV/Image Editor`.
- [ ] Place your mouse inside the right side 3d view and press kbd:[NumPad 5] to toggle ortho view. You're now setup to render your Angular map.
- [ ] Save your file.

[%interactive]
.Rendering And Saving
- [ ] With your first camera selected (in this case Camera-down) and your mouse inside the right side 3d view, press kbd:[Ctrl]+kbd:[NumPad 0] to set your selected camera to be the active camera.
- [ ] Press kbd:[F12] to render the scene. A image will appear in the left side UV/Image Editor.
- [ ] With your mouse inside the left side UV/Image Editor you can scroll in or out to center the view.
- [ ] With your mouse inside the left side UV/Image Editor press kbd:[F3] to save your image. Rename the image (down.jpg in this case).

Follow this same procedure for the remaining cameras. Rendering, renaming and saving each. After you have rendered all your images you can copy and paste them into your asset folder for JME3. Usually under the Texture directory. To use them in your code, in simpleInitApp(), load the Textures and use the SkyFactory to create your sky.

[source,java]
----
Texture west = getAssetManager().loadTexture("Textures/Sky/west.jpg");
Texture east = getAssetManager().loadTexture("Textures/Sky/east.jpg");
Texture north = getAssetManager().loadTexture("Textures/Sky/north.jpg");
Texture south = getAssetManager().loadTexture("Textures/Sky/south.jpg");
Texture up = getAssetManager().loadTexture("Textures/Sky/up.jpg");
Texture down = getAssetManager().loadTexture("Textures/Sky/down.jpg");
getRootNode().attachChild(SkyFactory.createSky(getAssetManager(), west, east, north, south, up, down));
----

Listed below are other Blender tutorials JME3 users may find valuable.

*  link:https://www.katsbits.com/tutorials/blender/cycles-skybox.php[Render a Skybox using Cycles]
*  link:https://www.katsbits.com/tutorials/blender/render-skybox.php[Render a Skybox Environment Map]

Many thanks go out to contributor glaucomardano for his video. He has excellent taste in music.

== Gimp

You can use link:https://www.gimp.org/[Gimp] to create SkyMaps from a single image with the addition of 2 scripts.

*  link:https://code.google.com/archive/p/gimp-dds/[Gimp-dds]
*  link:http://registry.gimp.org/node/25532[Cubemap Layers Generator]

After installing the scripts you open a image in gimp. This script works by slicing up the image into 6 layers of equal size, each by the power of 2.

.  After you open the image you select `menu:Filters[Generic > Cubemap Layers Generator]`.
.  Fill in the details as follows.
**  Source: navigate to the image you are slicing.
**  Cubemap layout: `Cross Horizontal`
**  2 to the power of: `10` (for 1024 sized Layers)
.  Press btn:[OK] to slice up the image.
.  Select `menu:File[Export As]` and change the `Name` and `File Type` to `.dds` and choose your save location to export into.
.  Press btn:[Export].
* Compression: `None`
* Format: `RGB8`
* Save: `As cube map`
* MipMaps: `No mipmaps`
. Press btn:[OK] to export.

You add it to your scene as is explained in the <<jme3/advanced/sky#,How to add a Sky to your Scene>> tutorial.

= How to create free SkyMaps
:author: Robert Allen
:revnumber:
:revdate: 2017/04/4 10:23
:relfileprefix: ../../
:imagesdir: ../..
:experimental:
ifdef::env-github,env-browser[:outfilesuffix: .adoc]

There are a plethora of ways to create skymaps with varying levels of difficulty and expense. The link:http://planetside.co.uk/[Terragen] program has been mentioned as one way to do so but it is now limited in its use and for commercial purposes is not free. Another program, link:https://www.daz3d.com/bryce-7-pro[Bryce], is also not free and seems to of stalled in development based off there only being a 32bit version available. Maybe they are doing things behind the scenes but its still not a free program. This articles intent is to give the JME3 user other options for the creation of skymaps using the free programs Blender and Gimp. It is not the be all, end all of skymap creation. If you know of better methods or tools please feel free to share your wisdom on the link:https://hub.jmonkeyengine.org/[forums].

== Blender

Using Blender you can create your skymaps for free. Once link:https://www.blender.org/[Blender] is setup, your skymaps creation can be done easily. For this article I will go into detail on how to setup Blender to create Angular maps based off a video produced by jMonkey contributor glaucomardano and mentioned in this link:https://hub.jmonkeyengine.org/t/jmonkeyengine-tutorial-how-to-create-skymaps-using-blender/19313[forum post]. Contributor glaucomardano did a good job on the video but it requires previous knowledge of Blender to be able to easily follow along. As such, I will translate it to paper for you. You can view his video by following this link: link:https://youtu.be/z38Aikz5nE8[jMonkeyEngine Tutorial - How to create skymaps using blender].

[NOTE]
====
This tutorial is an adaptation of contributor glaucomardanos production, not an exact duplicate. +
These steps assume you're using the default blender setup and apply to vers 2.78c+. +
This is in checklist format so you can mark items as you go to keep your place.
====

[%interactive]
.Blender Skybox Checklist
- [ ] Start with a new file in blender.
- [ ] Select the lamp and the default cube in the 3d view or in the Outliner panel (top-right panel) and delete them: kbd:[X]. They are not needed for this procedure.
- [ ] Select the camera object in the 3d view or in the Outliner panel then clear its location and rotation by using keyboard shortcuts kbd:[Alt]+kbd:[G] and kbd:[Alt]+kbd:[R].
- [ ] In the properties panel (lower-right panel) select the Object tab (orange box). This will give you a visual perspective of the camera changes you are about to make.
- [ ] Rename the Camera to Camera-north.
- [ ] Change your 3d view to top-ortho by pressing kbd:[NumPad 7] followed by kbd:[NumPad 5].
- [ ] With the camera still selected, press kbd:[R] immediately followed by kbd:[X] immediately followed by 90 immediately followed by kbd:[Enter] to rotate the camera 90 degrees around the X axis.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] Press kbd:[R] immediately followed by 180 immediately followed by kbd:[Enter]. This will rotate the camera 180 degrees around the Z axis. Rename this camera to Camera-south.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by 90 immediately followed by kbd:[Enter]. This will rotate the camera another 90 degrees around the Z axis. Rename this camera to Camera-west.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by -180 immediately followed by kbd:[Enter]. This will rotate the camera -180 degrees around the Z axis. Rename this camera to Camera-east.
- [ ] With your mouse inside the 3d view change your view to right-ortho by entering kbd:[NumPad 3].
- [ ] Select Camera-south in 3d view or in the Outliner panel (top-right panel) and press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by 90 immediately followed by kbd:[Enter]. This will rotate the camera 90 degrees around the X axis. The camera should now be pointing up. Rename this camera to Camera-up.
- [ ] With the camera still selected, press kbd:[Shift]+kbd:[D]. This will duplicate the camera. Next press kbd:[Enter] to set the selection.
- [ ] With the camera still selected, press kbd:[R] immediately followed by 180 immediately followed by kbd:[Enter]. This will rotate the camera 180 degrees along the X axis. The camera should now be facing down. Rename this camera to Camera-down.
- [ ] Save your file.
- [ ] Open up a web browser and search for "free high res skymap" and select a Angular map of your choice or you can find some here at link:https://blenderartists.org/forum/showthread.php?24038-Free-high-res-skymaps-%2528Massive-07-update!%2529[https://blenderartists.org/forum/showthread.php?24038-Free-high-res-skymaps-%2528Massive-07-update!%2529] instead. Remember to select an *ANGULAR* map.
- [ ] Save a map to your pc in a place you can easily locate it from later in blender.

[%interactive]
.Textures Tab
- [ ] From the properties panel (bottom-right panel) select the Textures tab (red-white checkerboard) then press the new button to create a new texture. Rename this texture to AngMap.
- [ ] Under the Image panel select open and navigate to the file you saved earlier.
- [ ] In the Mapping panel select AngMap from the drop down box.
- [ ] In the Influence panel de-select blend (ble) and select horizon (hor).

[%interactive]
.Data Tab
- [ ] From the properties panel select the Data tab (reel to reel camera).
- [ ] In the lens panel change the Focal Lens value from 35 to 16. Do this for every camera.

[%interactive]
.Render Tab
- [ ] From the properties panel select the Render tab (normal looking camera).
- [ ] In the Dimensions panel set the resolution to any number that is a power of 2. For this example 1024 x 1024.
- [ ] Slide the resolution scale to 100%.
- [ ] In the Output panel change the image type to JPEG. I have found out by trial and error that using a JPEG file has the same image quality as a PNG or DDS file but with a huge difference in image size. A single PNG image will clock in at over 8mb to the JPEG size of 325kb. Even converting to a DDS file comes in at over 3mb for comparison (using RGB888 as is recommended by Momoko_Fan/Core Developer in this link:https://hub.jmonkeyengine.org/t/best-dds-format-for-skyfactory/17668/2[forum post]) with no gain in image quality that I could see. Your welcome to experiment on your own if you wish.
- [ ] Set the image format to RGB.
- [ ] Setting the quality slider to 0 has the effect of reducing the image size. In this example it reduced the image sizes to less than 125kb, once again with no discernable image degradation that I could see.

[%interactive]
.World Tab
- [ ] In the properties panel select the World tab.
- [ ] Check the Real Sky toggle.

[%interactive]
.Map Generator Setup
- [ ] From the header at the top of the 3d view click the btn:[Choose Screen Layout] button next to the word `Default` and select `Split Verticle F/R`.
- [ ] Click the `+` sign to create a new layout.
- [ ] Rename this new layout Angular Map Generator or a name of your choosing.
- [ ] In the left side 3d view, at the bottom, next to the word view, is the btn:[Current Editor Type] button. Click it and change it to `UV/Image Editor`.
- [ ] Place your mouse inside the right side 3d view and press kbd:[NumPad 5] to toggle ortho view. You're now setup to render your Angular map.
- [ ] Save your file.

[%interactive]
.Rendering And Saving
- [ ] With your first camera selected (in this case Camera-down) and your mouse inside the right side 3d view, press kbd:[Ctrl]+kbd:[NumPad 0] to set your selected camera to be the active camera.
- [ ] Press kbd:[F12] to render the scene. A image will appear in the left side UV/Image Editor.
- [ ] With your mouse inside the left side UV/Image Editor you can scroll in or out to center the view.
- [ ] With your mouse inside the left side UV/Image Editor press kbd:[F3] to save your image. Rename the image (down.jpg in this case).

Follow this same procedure for the remaining cameras. Rendering, renaming and saving each. After you have rendered all your images you can copy and paste them into your asset folder for JME3. Usually under the Texture directory. To use them in your code, in simpleInitApp(), load the Textures and use the SkyFactory to create your sky.

[source,java]
----
Texture west = getAssetManager().loadTexture("Textures/Sky/west.jpg");
Texture east = getAssetManager().loadTexture("Textures/Sky/east.jpg");
Texture north = getAssetManager().loadTexture("Textures/Sky/north.jpg");
Texture south = getAssetManager().loadTexture("Textures/Sky/south.jpg");
Texture up = getAssetManager().loadTexture("Textures/Sky/up.jpg");
Texture down = getAssetManager().loadTexture("Textures/Sky/down.jpg");
getRootNode().attachChild(SkyFactory.createSky(getAssetManager(), west, east, north, south, up, down));
----

Listed below are other Blender tutorials JME3 users may find valuable.

*  link:https://www.katsbits.com/tutorials/blender/cycles-skybox.php[Render a Skybox using Cycles]
*  link:https://www.katsbits.com/tutorials/blender/render-skybox.php[Render a Skybox Environment Map]

Many thanks go out to contributor glaucomardano for his video. He has excellent taste in music.

== Gimp

You can use link:https://www.gimp.org/[Gimp] to create SkyMaps from a single image with the addition of 2 scripts.

*  link:https://code.google.com/archive/p/gimp-dds/[Gimp-dds]
*  link:http://registry.gimp.org/node/25532[Cubemap Layers Generator]

After installing the scripts you open a image in gimp. This script works by slicing up the image into 6 layers of equal size, each by the power of 2.

.  After you open the image you select `menu:Filters[Generic > Cubemap Layers Generator]`.
.  Fill in the details as follows.
**  Source: navigate to the image you are slicing.
**  Cubemap layout: `Cross Horizontal`
**  2 to the power of: `10` (for 1024 sized Layers)
.  Press btn:[OK] to slice up the image.
.  Select `menu:File[Export As]` and change the `Name` and `File Type` to `.dds` and choose your save location to export into.
.  Press btn:[Export].
* Compression: `None`
* Format: `RGB8`
* Save: `As cube map`
* MipMaps: `No mipmaps`
. Press btn:[OK] to export.

You add it to your scene as is explained in the <<jme3/advanced/sky#,How to add a Sky to your Scene>> tutorial.


Update 2014-02-28-Write-once-run-No-write-as-often-as-possible.adoc


Fixed broken link in the 2.5 changelog

Fixed broken link in the 2.5 changelog

[[changes-2-5]]
== Changes in Citrus 2.5

We have added lots of new features and improvements with Citrus 2.5. Namely these are the new modules for RMI and JMX support, a new x-www-form-urlencoded message validator and new functions anc test actions. Just have a look at the following features that made it to the box.

[[changes-hamcrest-matcher]]
=== Hamcrest matcher support

Hamcrest is a very powerful matcher library that provides a fantastic set of matcher implementations for message validation purpose. Citrus now supports these matchers coming from Hamcrest library. On the one hand you can use Hamcrest matchers as a Citrus validation matcher as described in link:validation-matcher-hamcrest[validation-matcher-hamcrest]. On the other hand you can use Hamcrest matchers now directly using the Citrus Java DSL. See details for this feature in link:json-path-validate[json-path-validate].

[[changes-binary-base64-message-validator]]
=== Binary base64 message validator

There is a new message validator implementation that automatically converts binary message content to a base64 encoded String representation for comparison. This is the easiest way to compare binary message content with an expected message payload. See link:#binary-message-validation[binary-message-validation] how this is working for you.

[[changes-rmi]]
=== RMI support

Remote method invocation is a standard Java technology and API for calling methods on remote objects across different JVM instances. Although RMI has lost its popularity it is still used in legacy components. Testing RMI bean invocation is a hard thing to do. Now Citrus provides client and server support for remote interface invocation. See link:rmi[rmi] for details.

[[changes-jmx]]
=== JMX support

Similar to RMI JMX can be used to connect to remote bean invocation. This time we expose some beans to a managed bean server in order to be managed by JMX operations for read and write. With Citrus 2.5 we have added a client and server support for calling and providing managed beans on a mbean server. See link:jmx[jmx] for details.

[[changes-resource-injection]]
=== Resource injection

With 2.5 we have added mechanisms for injecting Citrus components to your Java DSL test methods. This is very useful when needing access to the Citrus test context for instance. Also we are able to use new injection of test designer and runner instances in order to support parallel test execution with multiple threads. See the explanations in link:testcase-resource-injection[testcase-resource-injection]and link:testcase-context-injection[testcase-context-injection].

[[changes-http-x-www-form-urlencoded-message-validator]]
=== Http x-www-form-urlencoded message validator

HTML form data can be transmitted with different methods and content types. One of the most common ways is to use *x-www-form-urlencoded* form data content. As validation can be tricky we have added a special message validator for that. See link:http-www-form-urlencoded[http-www-form-urlencoded]for details.

[[changes-date-range-validation-matcher]]
=== Date range validation matcher

Added a new validation matcher implementation that is able to check that a date value is between a certain date range (from and to) The date range is able to focus on days as well as additional time (hour, minute, second) specifications. See link:validation-matcher-daterange[validation-matcher-daterange]for details.

[[changes-read-file-resource-function]]
=== Read file resource function

A new function implementation offers you the possibilities to read file resource contents as inline data. The function is called and returns the file content as return value. The file content is then placed right where the function was called e.g. inside of a message payload element or as message header value. See link:functions-read-file[functions-read-file]for details.

[[changes-timer-container]]
=== Timer container

The new timer test action container repeats its execution based on a time expression (e.g. every 5 seconds). With this timer we can repeat test actions with a fixed time delay or constantly execute test actions with time schedule. See link:containers-timer[containers-timer]and link:actions-stop-timer[actions-stop-timer]for details.

[[changes-upgrade-to-vertx-3-2-0]]
=== Upgrade to Vert.x 3.2.0

The Vert.x module was upgraded to use Vert.x 3.2.0 version. The Citrus module implementation was updated to work with this new Vert.x version. Learn more about the Vert.x integration in Citrus with link:vertx[vertx].

[[changes-2-5]]
== Changes in Citrus 2.5

We have added lots of new features and improvements with Citrus 2.5. Namely these are the new modules for RMI and JMX support, a new x-www-form-urlencoded message validator and new functions anc test actions. Just have a look at the following features that made it to the box.

[[changes-hamcrest-matcher]]
=== Hamcrest matcher support

Hamcrest is a very powerful matcher library that provides a fantastic set of matcher implementations for message validation purpose. Citrus now supports these matchers coming from Hamcrest library. On the one hand you can use Hamcrest matchers as a Citrus validation matcher as described in link:validation-matcher-hamcrest[validation-matcher-hamcrest]. On the other hand you can use Hamcrest matchers now directly using the Citrus Java DSL. See details for this feature in link:json-path-validate[json-path-validate].

[[changes-binary-base64-message-validator]]
=== Binary base64 message validator

There is a new message validator implementation that automatically converts binary message content to a base64 encoded String representation for comparison. This is the easiest way to compare binary message content with an expected message payload. See link:validation-binary[validation-binary]how this is working for you.

[[changes-rmi]]
=== RMI support

Remote method invocation is a standard Java technology and API for calling methods on remote objects across different JVM instances. Although RMI has lost its popularity it is still used in legacy components. Testing RMI bean invocation is a hard thing to do. Now Citrus provides client and server support for remote interface invocation. See link:rmi[rmi] for details.

[[changes-jmx]]
=== JMX support

Similar to RMI JMX can be used to connect to remote bean invocation. This time we expose some beans to a managed bean server in order to be managed by JMX operations for read and write. With Citrus 2.5 we have added a client and server support for calling and providing managed beans on a mbean server. See link:jmx[jmx] for details.

[[changes-resource-injection]]
=== Resource injection

With 2.5 we have added mechanisms for injecting Citrus components to your Java DSL test methods. This is very useful when needing access to the Citrus test context for instance. Also we are able to use new injection of test designer and runner instances in order to support parallel test execution with multiple threads. See the explanations in link:testcase-resource-injection[testcase-resource-injection]and link:testcase-context-injection[testcase-context-injection].

[[changes-http-x-www-form-urlencoded-message-validator]]
=== Http x-www-form-urlencoded message validator

HTML form data can be transmitted with different methods and content types. One of the most common ways is to use *x-www-form-urlencoded* form data content. As validation can be tricky we have added a special message validator for that. See link:http-www-form-urlencoded[http-www-form-urlencoded]for details.

[[changes-date-range-validation-matcher]]
=== Date range validation matcher

Added a new validation matcher implementation that is able to check that a date value is between a certain date range (from and to) The date range is able to focus on days as well as additional time (hour, minute, second) specifications. See link:validation-matcher-daterange[validation-matcher-daterange]for details.

[[changes-read-file-resource-function]]
=== Read file resource function

A new function implementation offers you the possibilities to read file resource contents as inline data. The function is called and returns the file content as return value. The file content is then placed right where the function was called e.g. inside of a message payload element or as message header value. See link:functions-read-file[functions-read-file]for details.

[[changes-timer-container]]
=== Timer container

The new timer test action container repeats its execution based on a time expression (e.g. every 5 seconds). With this timer we can repeat test actions with a fixed time delay or constantly execute test actions with time schedule. See link:containers-timer[containers-timer]and link:actions-stop-timer[actions-stop-timer]for details.

[[changes-upgrade-to-vertx-3-2-0]]
=== Upgrade to Vert.x 3.2.0

The Vert.x module was upgraded to use Vert.x 3.2.0 version. The Citrus module implementation was updated to work with this new Vert.x version. Learn more about the Vert.x integration in Citrus with link:vertx[vertx].


Fix alternative names example in documentation (#3863)

Signed-off-by: Jakub Scholz <c50267b906a652f2142cfab006e215c9f6fdc8a0@scholzj.com>

Configures bootstrap service overrides for external listeners.

Broker service equivalents of `nodePort`, `host`, `loadBalancerIP` and `annotations` properties are configured in the xref:type-GenericKafkaListenerConfigurationBroker-reference[`GenericKafkaListenerConfigurationBroker` schema].

[id='property-listener-config-altnames-{context}']
=== `alternativeNames`

You can specify alternative names for the bootstrap service.
The names are added to the broker certificates and can be used for TLS hostname verification.
The `alternativeNames` property is applicable to all types of external listeners.

.Example of an external `route` listener configured with an additional bootstrap address
[source,yaml,subs="attributes+"]
----
listeners:
  #...
  - name: external
    port: 9094
    type: route
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        alternativeNames:
          - example.hostname1
          - example.hostname2
# ...
----

[id='property-listener-config-host-{context}']
=== `host`

The `host` property is used with `route` and `ingress` listeners to specify the hostnames used by the bootstrap and per-broker services.

A `host` property value is mandatory for `ingress` listener configuration, as the Ingress controller does not assign any hostnames automatically.
Make sure that the hostnames resolve to the Ingress endpoints.
Strimzi will not perform any validation that the requested hosts are available and properly routed to the Ingress endpoints.

.Example of host configuration for an ingress listener
[source,yaml,subs="attributes+"]
----
listeners:
  #...
  - name: external
    port: 9094
    type: ingress
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        host: bootstrap.myingress.com
      brokers:
      - broker: 0
        host: broker-0.myingress.com
      - broker: 1
        host: broker-1.myingress.com
      - broker: 2
        host: broker-2.myingress.com
# ...
----

By default, `route` listener hosts are automatically assigned by OpenShift.
However, you can override the assigned route hosts by specifying hosts.

Strimzi does not perform any validation that the requested hosts are available.
You must ensure that they are free and can be used.

.Example of host configuration for a route listener
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: route
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        host: bootstrap.myrouter.com
      brokers:
      - broker: 0
        host: broker-0.myrouter.com
      - broker: 1
        host: broker-1.myrouter.com
      - broker: 2
        host: broker-2.myrouter.com
# ...
----

[id='property-listener-config-nodeport-{context}']
=== `nodePort`

By default, the port numbers used for the bootstrap and broker services are automatically assigned by Kubernetes.
You can override the assigned node ports for `nodeport` listeners by specifying the requested port numbers.

Strimzi does not perform any validation on the requested ports.
You must ensure that they are free and available for use.

.Example of an external listener configured with overrides for node ports
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: nodeport
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        nodePort: 32100
      brokers:
      - broker: 0
        nodePort: 32000
      - broker: 1
        nodePort: 32001
      - broker: 2
        nodePort: 32002
# ...
----

[id='property-listener-config-lb-ip-{context}']
=== `loadBalancerIP`

Use the `loadBalancerIP` property to request a specific IP address when creating a loadbalancer.
Use this property when you need to use a loadbalancer with a specific IP address.
The `loadBalancerIP` field is ignored if the cloud provider does not support the feature.

.Example of an external listener of type `loadbalancer` with specific loadbalancer IP address requests
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: loadbalancer
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        loadBalancerIP: 172.29.3.10
      brokers:
      - broker: 0
        loadBalancerIP: 172.29.3.1
      - broker: 1
        loadBalancerIP: 172.29.3.2
      - broker: 2
        loadBalancerIP: 172.29.3.3
# ...
----

[id='property-listener-config-annotations-{context}']
=== `annotations`

Use the `annotations` property to add annotations to `loadbalancer`, `nodeport` or `ingress` listeners.
You can use these annotations to instrument DNS tooling such as {KubernetesExternalDNS}, which automatically assigns DNS names to the loadbalancer services.

.Example of an external listener of type `loadbalancer` using `annotations`
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: loadbalancer
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-bootstrap.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
      brokers:
      - broker: 0
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-broker-0.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
      - broker: 1
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-broker-1.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
      - broker: 2
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-broker-2.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
# ...
----

Configures bootstrap service overrides for external listeners.

Broker service equivalents of `nodePort`, `host`, `loadBalancerIP` and `annotations` properties are configured in the xref:type-GenericKafkaListenerConfigurationBroker-reference[`GenericKafkaListenerConfigurationBroker` schema].

[id='property-listener-config-altnames-{context}']
=== `alternativeNames`

You can specify alternative names for the bootstrap service.
The names are added to the broker certificates and can be used for TLS hostname verification.
The `alternativeNames` property is applicable to all types of external listeners.

.Example of an external `route` listener configured with an additional bootstrap address
[source,yaml,subs="attributes+"]
----
listeners:
  #...
  - name: external
    port: 9094
    type: route
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        alternativeNames: example.hostname
# ...
----

[id='property-listener-config-host-{context}']
=== `host`

The `host` property is used with `route` and `ingress` listeners to specify the hostnames used by the bootstrap and per-broker services.

A `host` property value is mandatory for `ingress` listener configuration, as the Ingress controller does not assign any hostnames automatically.
Make sure that the hostnames resolve to the Ingress endpoints.
Strimzi will not perform any validation that the requested hosts are available and properly routed to the Ingress endpoints.

.Example of host configuration for an ingress listener
[source,yaml,subs="attributes+"]
----
listeners:
  #...
  - name: external
    port: 9094
    type: ingress
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        host: bootstrap.myingress.com
      brokers:
      - broker: 0
        host: broker-0.myingress.com
      - broker: 1
        host: broker-1.myingress.com
      - broker: 2
        host: broker-2.myingress.com
# ...
----

By default, `route` listener hosts are automatically assigned by OpenShift.
However, you can override the assigned route hosts by specifying hosts.

Strimzi does not perform any validation that the requested hosts are available.
You must ensure that they are free and can be used.

.Example of host configuration for a route listener
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: route
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        host: bootstrap.myrouter.com
      brokers:
      - broker: 0
        host: broker-0.myrouter.com
      - broker: 1
        host: broker-1.myrouter.com
      - broker: 2
        host: broker-2.myrouter.com
# ...
----

[id='property-listener-config-nodeport-{context}']
=== `nodePort`

By default, the port numbers used for the bootstrap and broker services are automatically assigned by Kubernetes.
You can override the assigned node ports for `nodeport` listeners by specifying the requested port numbers.

Strimzi does not perform any validation on the requested ports.
You must ensure that they are free and available for use.

.Example of an external listener configured with overrides for node ports
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: nodeport
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        nodePort: 32100
      brokers:
      - broker: 0
        nodePort: 32000
      - broker: 1
        nodePort: 32001
      - broker: 2
        nodePort: 32002
# ...
----

[id='property-listener-config-lb-ip-{context}']
=== `loadBalancerIP`

Use the `loadBalancerIP` property to request a specific IP address when creating a loadbalancer.
Use this property when you need to use a loadbalancer with a specific IP address.
The `loadBalancerIP` field is ignored if the cloud provider does not support the feature.

.Example of an external listener of type `loadbalancer` with specific loadbalancer IP address requests
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: loadbalancer
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        loadBalancerIP: 172.29.3.10
      brokers:
      - broker: 0
        loadBalancerIP: 172.29.3.1
      - broker: 1
        loadBalancerIP: 172.29.3.2
      - broker: 2
        loadBalancerIP: 172.29.3.3
# ...
----

[id='property-listener-config-annotations-{context}']
=== `annotations`

Use the `annotations` property to add annotations to `loadbalancer`, `nodeport` or `ingress` listeners.
You can use these annotations to instrument DNS tooling such as {KubernetesExternalDNS}, which automatically assigns DNS names to the loadbalancer services.

.Example of an external listener of type `loadbalancer` using `annotations`
[source,yaml,subs="attributes+"]
----
# ...
listeners:
  #...
  - name: external
    port: 9094
    type: loadbalancer
    tls: true
    authentication:
      type: tls
    configuration:
      bootstrap:
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-bootstrap.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
      brokers:
      - broker: 0
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-broker-0.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
      - broker: 1
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-broker-1.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
      - broker: 2
        annotations:
          external-dns.alpha.kubernetes.io/hostname: kafka-broker-2.mydomain.com.
          external-dns.alpha.kubernetes.io/ttl: "60"
# ...
----