<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Marius Barbulescu's Blog]]></title><description><![CDATA[Marius Barbulescu's Blog]]></description><link>https://marius-barbulescu.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 16 Sep 2026 01:27:57 GMT</lastBuildDate><atom:link href="https://marius-barbulescu.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Apache Camel - the dark side]]></title><description><![CDATA[Apache Camel is a popular integration framework with a long history and a good community around. It implements many integration patterns and has a big list of ready-to-use integrations.
It is very possible that I didn't get it or I didn't spend enoug...]]></description><link>https://marius-barbulescu.hashnode.dev/apache-camel-the-dark-side</link><guid isPermaLink="true">https://marius-barbulescu.hashnode.dev/apache-camel-the-dark-side</guid><category><![CDATA[integration]]></category><dc:creator><![CDATA[Marius Barbulescu]]></dc:creator><pubDate>Sun, 16 Jan 2022 06:12:26 GMT</pubDate><content:encoded><![CDATA[<p><a target="_blank" href="https://camel.apache.org/">Apache Camel</a> is a popular integration framework with a long history and a good community around. It implements many integration patterns and has a big list of ready-to-use integrations.</p>
<p>It is very possible that I didn't get it or I didn't spend enough time working with it, but my experience with Camel is not great. I worked with Camel in two different teams with a different set of skills and experience but all Camel projects had several characteristics: </p>
<ul>
<li>no tests or tests in bad shape</li>
<li>low type safety due to large number of casts</li>
<li>huge and complex routes</li>
</ul>
<p>After a lot of pain, we decided to drop Camel and to use plain Spring Boot and a few things changed:</p>
<ul>
<li>it became easier to write tests</li>
<li>easier to design and write the code</li>
<li>team happiness increased</li>
</ul>
<p>Even after this, I don't think Camel is a bad framework and it can be successful in certain contexts. It is important to understand its good and bad parts and decide for yourself. I will highlight its bad parts as its good parts are easy to find <a target="_blank" href="https://www.openlogic.com/blog/what-apache-camel">e.g.</a></p>
<h4 id="heading-type-safety">type safety</h4>
<p><a target="_blank" href="https://www.javadoc.io/doc/org.apache.camel/camel-api/latest/org/apache/camel/Exchange.html">Exchange</a> is central to the route and it provides no type safety. It is too easy to expect a rabbit but to get an orange. Exchange is like a raw Map but producer and consumer are most of usually in different files which makes this hard to spot.</p>
<h4 id="heading-testing">testing</h4>
<p>It was really difficult to write tests with Camel. Mocking did not work as expected, code to be tested was very coupled and negative testing was almost impossible.
The resulting test was usually very complicated and very difficult to read. The number of disabled tests grew constantly as it was difficult to understand what went wrong.</p>
<h4 id="heading-surprising-behavior">surprising behavior</h4>
<p>Nobody is probably happy when the behavior of a certain piece of code surprises. Something like a <code>printReport</code> method does also some updates in a database. Or when exceptions are swallowed and everything looks rosy. It was quite a surprise to find that this is the default Camel exception handling.</p>
<h4 id="heading-abstraction-inversion">abstraction inversion</h4>
<p>It gets really funny when people start implementing everything in routes. Do we need to add two numbers? Let's create a new route. We start coding in Camel DSL and things are getting complicated. Let's add more routes to solve this.
In the end, something which can be done in a few lines of Java code becomes a huge untested route.</p>
<h4 id="heading-learning-curve">learning curve</h4>
<p>We got so many times in situations where we knew something can be solved with plain Spring but we were in a route context and we had to find a Camel solution. Hence we spent hours and sometimes days doing something which could have been done in minutes.
And so many times we realized later that the solution we found was not doing what we expected to do.</p>
<h4 id="heading-design">design</h4>
<p>Camel seemed to stay in your way and not let you design code in the way wanted.
We found it especially difficult to implement wrapping logic. Think about <code>try-finally</code> Java concept.  We had to transform a local wrapping logic into a linear logic having its endpoints far away in the code.</p>
<h4 id="heading-custom-integration">custom integration</h4>
<p>We had a case of one request - many responses pattern on a JMS interface. Camel supports only one request - one response pattern so the team had to work around this. A very unmaintainable solution was found for something which was done later in a different project very simple with plain Spring solution.</p>
<h4 id="heading-combining-frameworks">combining frameworks</h4>
<p>We must always be careful when we add libraries to our projects. Adding 2 similar frameworks like Camel and Spring to a project is close to insanity. You'll find all crazy combinations of these 2 frameworks and interesting problems to solve when conflicts between libraries happen.</p>
<p>In closing words, be very careful when you decide what framework to use in your project. Question everything, start small and grow the solution slowly.</p>
]]></content:encoded></item><item><title><![CDATA[Git in Git]]></title><description><![CDATA[I had recently to ramp up my Git knowledge and while I was trying to understand how Git really works, I recalled a Meetup event I attended some time ago.
Pablo Vergés presented at the Software-Craftsmanship-Zurich event an interesting approach on lea...]]></description><link>https://marius-barbulescu.hashnode.dev/git-in-git</link><guid isPermaLink="true">https://marius-barbulescu.hashnode.dev/git-in-git</guid><category><![CDATA[Git]]></category><dc:creator><![CDATA[Marius Barbulescu]]></dc:creator><pubDate>Sat, 15 Jan 2022 19:09:51 GMT</pubDate><content:encoded><![CDATA[<p>I had recently to ramp up my Git knowledge and while I was trying to understand how Git really works, I recalled a Meetup event I attended some time ago.</p>
<p><a target="_blank" href="https://github.com/escodebar">Pablo Vergés</a> presented at the <a target="_blank" href="https://www.meetup.com/Software-Craftsmanship-Zurich/events/261931385/">Software-Craftsmanship-Zurich</a> event an interesting approach on learning Git.</p>
<p>The idea is to create a git repository inside another git repository and track changes in the inner repository using the outer repository. </p>
<p>Slides can be found <a target="_blank" href="https://escodebar.github.io/trainings/git/meetup/#/">here</a> and a recording of another event with the same content can be found <a target="_blank" href="https://www.youtube.com/watch?v=T4pPa99LXJk">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[JUnit Spring extension]]></title><description><![CDATA[Writing a plain JUnit extension is quite a straightforward task and it is described well in user-guide.
It gets a bit more complicated when we try to inject Spring components into the integration testing context.
Let's try to clean up embedded Elasti...]]></description><link>https://marius-barbulescu.hashnode.dev/junit-spring-extension</link><guid isPermaLink="true">https://marius-barbulescu.hashnode.dev/junit-spring-extension</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[Spring]]></category><category><![CDATA[junit]]></category><dc:creator><![CDATA[Marius Barbulescu]]></dc:creator><pubDate>Thu, 06 Jan 2022 05:36:23 GMT</pubDate><content:encoded><![CDATA[<p>Writing a plain JUnit extension is quite a straightforward task and it is described well in <a target="_blank" href="https://junit.org/junit5/docs/current/user-guide/#extensions">user-guide</a>.
It gets a bit more complicated when we try to inject Spring components into the integration testing context.</p>
<p>Let's try to clean up embedded Elasticsearch indexes before every integration test:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ElasticsearchCleanerExtension</span> : <span class="hljs-type">BeforeEachCallback {</span></span>

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">beforeEach</span><span class="hljs-params">(context: <span class="hljs-type">ExtensionContext</span>)</span></span> {
        <span class="hljs-keyword">val</span> contextManager = testContextManager(context)
        <span class="hljs-keyword">val</span> applicationContext = contextManager.testContext.applicationContext <span class="hljs-keyword">as</span> GenericApplicationContext
        <span class="hljs-keyword">val</span> cleaner = applicationContext.getBean(ElasticsearchCleaner::<span class="hljs-keyword">class</span>.java)
        cleaner.cleanup()
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">testContextManager</span><span class="hljs-params">(context: <span class="hljs-type">ExtensionContext</span>)</span></span>:TestContextManager {
        <span class="hljs-keyword">val</span> namespace = Namespace.create(SpringExtension::<span class="hljs-keyword">class</span>.java)
        <span class="hljs-keyword">val</span> store = context.getStore(namespace)
        <span class="hljs-keyword">return</span> store.getOrComputeIfAbsent(context.requiredTestClass, ::TestContextManager, TestContextManager::<span class="hljs-keyword">class</span>.java)
    }
}
</code></pre>
<p>which depends on the actual clean up component:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Component</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ElasticsearchCleaner</span></span>(<span class="hljs-keyword">val</span> studentRepository: StudentRepository) {
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">cleanup</span><span class="hljs-params">()</span></span> {
        studentRepository.deleteAll()
            .block(Duration.ofSeconds(<span class="hljs-number">2</span>))
    }
}
</code></pre>
<p>Each test must be annotated:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@SpringBootTest</span>
<span class="hljs-meta">@ExtendWith(ElasticsearchCleanerExtension::class)</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StudentPersistenceTest</span> </span>{
....
}
</code></pre>
<p>Now, the student index will be cleaned up before each test in the <code>StudentPersistenceTest</code> class.</p>
]]></content:encoded></item><item><title><![CDATA[How to customize Avro generated code]]></title><description><![CDATA[One common way to use Avro is to generate Java code from the Avro schema. This works well until we start using the generated code and we face difficulties in having a proper design on top of it.
Luckily, there is a way to customize the generated code...]]></description><link>https://marius-barbulescu.hashnode.dev/how-to-customize-avro-generated-code</link><guid isPermaLink="true">https://marius-barbulescu.hashnode.dev/how-to-customize-avro-generated-code</guid><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Marius Barbulescu]]></dc:creator><pubDate>Thu, 06 Jan 2022 05:34:50 GMT</pubDate><content:encoded><![CDATA[<p>One common way to use Avro is to generate Java code from the Avro schema. This works well until we start using the generated code and we face difficulties in having a proper design on top of it.</p>
<p>Luckily, there is a way to customize the generated code by the Maven plugin. We'll need 2 projects for this: one with customizations and the other with actual code generation.</p>
<h3 id="heading-customization-project">Customization project</h3>
<p>Let's try to add static constructors for single field types:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AvroGeneratorExtensions</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">staticConstructor</span><span class="hljs-params">(SpecificCompiler specificCompiler, Schema schema)</span> </span>{
        <span class="hljs-keyword">if</span> (schema.getFields().size() != <span class="hljs-number">1</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;
        }
        String parameters = createParameters(specificCompiler, schema);
        String setters = createSetters(schema);
        <span class="hljs-keyword">return</span> <span class="hljs-string">"public static "</span> + specificCompiler.javaType(schema) + <span class="hljs-string">" of("</span> + parameters + <span class="hljs-string">") {return newBuilder()"</span> + setters + <span class="hljs-string">".build();}"</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> String <span class="hljs-title">createSetters</span><span class="hljs-params">(Schema schema)</span> </span>{
        String setters = schema.getFields().stream()
                .map(f -&gt; <span class="hljs-string">"."</span> + generateSetMethod(schema, f) + <span class="hljs-string">"("</span> + f.name() + <span class="hljs-string">")"</span>)
                .collect(joining(<span class="hljs-string">"\n"</span>));
        <span class="hljs-keyword">return</span> setters;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> String <span class="hljs-title">createParameters</span><span class="hljs-params">(SpecificCompiler specificCompiler, Schema schema)</span> </span>{
        String parameters = schema.getFields().stream()
                .map(f -&gt; specificCompiler.javaType(f.schema()) + <span class="hljs-string">" "</span> + f.name())
                .collect(joining(<span class="hljs-string">", "</span>));
        <span class="hljs-keyword">return</span> parameters;
    }
</code></pre>
<p>This class must be compiled and published as a jar file.</p>
<h3 id="heading-messages-project">Messages project</h3>
<p>In the main project, we need to configure the build like:</p>
<pre><code class="lang-xml">    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.avro<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>avro-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>${avro.version}<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
                        <span class="hljs-comment">&lt;!-- add above project here --&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">executions</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">execution</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">phase</span>&gt;</span>generate-sources<span class="hljs-tag">&lt;/<span class="hljs-name">phase</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">goals</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>schema<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>protocol<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>idl-protocol<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">goals</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">sourceDirectory</span>&gt;</span>${project.basedir}/src/main/avro/<span class="hljs-tag">&lt;/<span class="hljs-name">sourceDirectory</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">templateDirectory</span>&gt;</span>${project.basedir}/src/main/resources/templates/<span class="hljs-tag">&lt;/<span class="hljs-name">templateDirectory</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">stringType</span>&gt;</span>String<span class="hljs-tag">&lt;/<span class="hljs-name">stringType</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">fieldVisibility</span>&gt;</span>private<span class="hljs-tag">&lt;/<span class="hljs-name">fieldVisibility</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">velocityToolsClassesNames</span>&gt;</span>AvroGeneratorExtensions<span class="hljs-tag">&lt;/<span class="hljs-name">velocityToolsClassesNames</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">execution</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">executions</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
</code></pre>
<p>Velocity templates must be extracted from avro jar file into <code>src/main/resources/templates/</code>. These are: </p>
<ul>
<li>enum.vm</li>
<li>fixed.vm</li>
<li>protocol.vm</li>
<li>record.vm</li>
</ul>
<p>Add <code>$avrogeneratorextensions.staticConstructor($this, $schema)</code> to record.vm to generate the static constructors.</p>
<p>A working example can be found here:</p>
<ul>
<li><a target="_blank" href="https://gitlab.com/barbulescu/avro-playground/-/tree/main/customtool">Customization</a></li>
<li><a target="_blank" href="https://gitlab.com/barbulescu/avro-playground/-/tree/main/customization">Messages</a></li>
</ul>
<p>Yeah, this looks like a huge hack but I am not aware of a better way to customize generated code.
Templates must be updated each time a new version of Avro library is used and the development process is basically trial and error.</p>
]]></content:encoded></item><item><title><![CDATA[Spring Data Elasticsearch custom converters]]></title><description><![CDATA[It happens quite often to have Elasticsearch fields with a small number of possible values so it is natural to model these as enums. 
Spring Data maps enum by default using the enum name and it gets a bit complicated when we want to map these values ...]]></description><link>https://marius-barbulescu.hashnode.dev/spring-data-elasticsearch-custom-converters</link><guid isPermaLink="true">https://marius-barbulescu.hashnode.dev/spring-data-elasticsearch-custom-converters</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[Spring]]></category><dc:creator><![CDATA[Marius Barbulescu]]></dc:creator><pubDate>Thu, 06 Jan 2022 05:17:27 GMT</pubDate><content:encoded><![CDATA[<p>It happens quite often to have Elasticsearch fields with a small number of possible values so it is natural to model these as enums. </p>
<p>Spring Data maps enum by default using the enum name and it gets a bit complicated when we want to map these values to a human-readable form.</p>
<p>Let's take as example simplified Gender enumeration:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">enum</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Gender</span></span>(<span class="hljs-keyword">val</span> esValue: String) {
    MALE(<span class="hljs-string">"m"</span>),
    FEMALE(<span class="hljs-string">"f"</span>),
    OTHER(<span class="hljs-string">"o"</span>);
}
</code></pre>
<p>We need to write 2 converters, one for serialization and one for deserialization:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@ReadingConverter</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringToGenderConverter</span> : <span class="hljs-type">Converter</span>&lt;<span class="hljs-type">String, Gender</span>&gt; </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> mapping: Map&lt;String, Gender&gt; = Gender.values().associateBy { it.esValue }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">convert</span><span class="hljs-params">(source: <span class="hljs-type">String</span>)</span></span>: Gender {
        <span class="hljs-keyword">return</span> mapping.getOrDefault(source, OTHER)
    }
}

<span class="hljs-meta">@WritingConverter</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GenderToStringConverter</span> : <span class="hljs-type">Converter</span>&lt;<span class="hljs-type">Gender, String</span>&gt; </span>{

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">convert</span><span class="hljs-params">(source: <span class="hljs-type">Gender</span>)</span></span>: String {
        <span class="hljs-keyword">return</span> source.esValue
    }
}
</code></pre>
<p>And then we need to add them to configuration:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Configuration</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ElasticsearchConfig</span></span>(<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> conversionService: GenericConversionService) : AbstractReactiveElasticsearchConfiguration() {

    <span class="hljs-meta">@Bean</span>
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">entityMapper</span><span class="hljs-params">()</span></span>: EntityMapper {
        <span class="hljs-keyword">val</span> entityMapper = ElasticsearchEntityMapper(elasticsearchMappingContext(), conversionService)
        <span class="hljs-keyword">val</span> converters = listOf(
            GenderToStringConverter(),
            StringToGenderConverter()
        )
        entityMapper.setConversions(ElasticsearchCustomConversions(converters))
        <span class="hljs-keyword">return</span> entityMapper
    }
}
</code></pre>
<p><code>GenderToStringConverter</code> can be made generic by having Gender implement an interface:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">enum</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Gender</span></span>(<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> esValue: String) : ElasticsearchValue {
    MALE(<span class="hljs-string">"m"</span>),
    FEMALE(<span class="hljs-string">"f"</span>),
    OTHER(<span class="hljs-string">"o"</span>);

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">esValue</span><span class="hljs-params">()</span></span>: String = esValue
}

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ElasticsearchValue</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">esValue</span><span class="hljs-params">()</span></span> : String
}
</code></pre>
<p>and converter:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@WritingConverter</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ElasticsearchValueToStringConverter</span> : <span class="hljs-type">Converter</span>&lt;<span class="hljs-type">ElasticsearchValue, String</span>&gt; </span>{

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">convert</span><span class="hljs-params">(source: <span class="hljs-type">ElasticsearchValue</span>)</span></span>: String {
        <span class="hljs-keyword">return</span> source.esValue()
    }
}
</code></pre>
]]></content:encoded></item></channel></rss>