<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matija Kovaček</title>
    <description>The latest articles on DEV Community by Matija Kovaček (@mkovacek).</description>
    <link>https://dev.to/mkovacek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F364699%2F5fbaf627-1cc3-418e-beae-1c458f340a5f.jpg</url>
      <title>DEV Community: Matija Kovaček</title>
      <link>https://dev.to/mkovacek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mkovacek"/>
    <language>en</language>
    <item>
      <title>Importance of Code reviews</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Wed, 12 Jan 2022 19:00:00 +0000</pubDate>
      <link>https://dev.to/ibmix/importance-of-code-reviews-163p</link>
      <guid>https://dev.to/ibmix/importance-of-code-reviews-163p</guid>
      <description>&lt;p&gt;Inspired by some of the last few projects, I have noticed that still a lot of people don't consider &lt;strong&gt;Code Review&lt;/strong&gt; seriously.&lt;/p&gt;

&lt;p&gt;From extreme cases where code review doesn't exist at all or where pull/merge requests are approved in a few seconds after being opened. Over the cases where the minority of team members care and perform code reviews, and the rest of the team members ignore them. The funny thing is when they are explicitly asked to check it, they consider them as "punishment".&lt;/p&gt;

&lt;p&gt;Luckily, one of my first projects had a really good code review process where all team members were very active and performed strict but fair code reviews. On that project, I learned a lot, learned why this process is so important and gained really good habits on how to perform code reviews.&lt;/p&gt;

&lt;p&gt;That's probably one of the reasons why I'm writing this today.&lt;/p&gt;

&lt;p&gt;Recently I was preparing guidelines and best practices about code reviews so I decided to share them with you as well.&lt;/p&gt;

&lt;p&gt;The things you will see here are not only my statements, they are inspired by other people, teams, and companies. But with those statements, I totally agree and I stand behind them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. So what is code review?
&lt;/h2&gt;

&lt;p&gt;A code review is a process where someone other than the author(s) of a piece of code examines that code. Code review should be used to maintain the quality of our code and products.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Process Flow
&lt;/h2&gt;

&lt;p&gt;Once you're satisfied and proud with your implementation and the tests, you create a pull request. With a pull request, you are requesting the merge of your local branch into the main source branch.&lt;/p&gt;

&lt;p&gt;All dev team members (including Leads / Architects) should be default reviewers of the pull request. The reviewer's job is to check if all requested functionalities are properly implemented and tested. She/He can suggest code improvements like using some library instead of reinventing the wheel, better naming for components and variables, and any other improvement that she/he can suggest.&lt;/p&gt;

&lt;p&gt;It is important that you shouldn't be disappointed if you receive a lot of comments on your pull requests. You will learn from the feedback and the solution will be better.&lt;/p&gt;

&lt;p&gt;When the minimum required number of reviewers have approved the pull request you can merge it. Don’t forget to delete your source (feature/bugfix) branch and move the ticket that is ready for QA (after it's deployed).&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Author of the pull request
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal&lt;/strong&gt; : Learn, improve your code quality, morale, and your working relationships.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.1. REVIEW your own code first
&lt;/h4&gt;

&lt;p&gt;Before sending code to your teammate, read it yourself and check the things that you would check as a code reviewer&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2. Be UP TO DATE with the develop branch state
&lt;/h4&gt;

&lt;p&gt;The recommendation is always to be up to date with the latest state from develop branch. This means that during the day you should pull changes several times and merge it to your local feature branch&lt;/p&gt;

&lt;p&gt;Before creating a pull request make sure that your feature/bugfix branch is up to date&lt;/p&gt;

&lt;h4&gt;
  
  
  3.3. Pull request should be DESCRIPTIVE
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Branch Name&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Source branch name should consist of branch type (feature, bugfix, hotfix), Jira ticket number, and Jira ticket title&lt;/li&gt;
&lt;li&gt;e.g feature/ticket123-some-ticket-title&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit messages&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Commit early, commit often with a descriptive message what you did&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull request description&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Add an additional summary of what is being done if it’s not clear from the pull request title&lt;/li&gt;
&lt;li&gt;Point out not so obvious/logical decisions and agreements&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comment your decisions&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Comment yourself (inline in code) for not so obvious/logical decisions, so that reviewers can easily understand it
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.4 Prefer SMALLER pull requests
&lt;/h4&gt;

&lt;p&gt;Try to make your pull request small as possible.&lt;/p&gt;

&lt;p&gt;Smaller pull requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easier to review and understand&lt;/li&gt;
&lt;li&gt;faster to review&lt;/li&gt;
&lt;li&gt;less chance to miss something&lt;/li&gt;
&lt;li&gt;less chance to create a bug
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.5. Respond graciously to feedback and LEARN from it
&lt;/h4&gt;

&lt;p&gt;Don’t take personally a code review feedback. Treat your reviewer’s notes as an objective discussion about the code.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.6. Be PATIENT when your reviewer is wrong
&lt;/h4&gt;

&lt;p&gt;From time to time, reviewers are wrong. Just as you can accidentally write buggy code, your reviewer can misunderstand the correct code.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.7. COMMUNICATE your responses explicitly
&lt;/h4&gt;

&lt;p&gt;For every comment that requires action, respond explicitly to confirm that you’ve addressed it. Some code review tools allow you to mark comments as resolved. Otherwise, follow a simple convention, like, "Done", for each note. If you disagree with the note, politely explain why you declined to take action.&lt;/p&gt;

&lt;p&gt;Don’t directly ask/chat with the reviewer about every comment. Commenting and discussing directly in the pull request is useful for other reviewers as well.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.8. MINIMIZE lag between rounds of review
&lt;/h4&gt;

&lt;p&gt;Once you create a pull request, driving the review to completion should be your highest priority. Delays on your end, will waste time for your reviewer, and increase complexity for your whole team.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.9. RESOLVE Conflicts
&lt;/h4&gt;

&lt;p&gt;In case of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;disagreements&lt;/li&gt;
&lt;li&gt;totally wrongly implemented features&lt;/li&gt;
&lt;li&gt;too many mistakes&lt;/li&gt;
&lt;li&gt;doubts, unclear things, or comments
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;schedule 1 on 1 call with the reviewer to resolve all doubts and unclear things. Make sure that you comment back agreements directly in a pull request for other reviewers.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Code reviewers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Mentor, educate and improve your working relationships.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.1. What do Code Reviewers look for&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Design&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Is the code well-designed and appropriate for your system?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functionality&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Does the code behave as the author likely intended?&lt;/li&gt;
&lt;li&gt;Is the way the code behaves good for its users (end-users and developers)?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Could the code be made simpler?&lt;/li&gt;
&lt;li&gt;Would another developer be able to easily understand and use this code when they come across it in the future?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Does the code have correct and well-designed tests?&lt;/li&gt;
&lt;li&gt;Do the tests cover requirements?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naming&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Did the developer choose clear names for variables, classes, methods?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comments&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Are the comments clear and useful?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Style&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Does the code follow the company style guide?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Did the developer update relevant documentation?
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.2. Don’t criticize, MENTOR and EDUCATE&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Code review is a chance for developers to mentor and teach others.&lt;/p&gt;

&lt;p&gt;When you comment on someone's pull request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;be kind&lt;/li&gt;
&lt;li&gt;instead of criticizing, ask why decisions were made like that&lt;/li&gt;
&lt;li&gt;suggest improvements&lt;/li&gt;
&lt;li&gt;explain your reasoning&lt;/li&gt;
&lt;li&gt;balance giving explicit directions with just pointing out problems and letting the developer decide
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.3. PRAISE good code&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Remember to praise good code and decisions. Don't only point out problems and suggestions.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.4. Don’t ignore pull requests&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Don’t ignore pull requests and think someone instead of you will do the review.&lt;/p&gt;

&lt;p&gt;Make a habit of doing code reviews like in the morning before daily, around lunchtime, before the end of the working day.&lt;/p&gt;

&lt;p&gt;If you are in the middle of a focused task, such as writing code, don’t interrupt yourself to do a code review. Instead, wait for a breakpoint in your work before you respond to a request for review.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.5. Less experienced team members don’t be shy&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Your opinion is important and expected here as well. More experienced team members don't know everything and they also make mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Speed of Code Reviews
&lt;/h2&gt;

&lt;p&gt;The speed of individual development is important, but not as important as the speed of the entire team.&lt;/p&gt;

&lt;p&gt;The speed of the team as a whole will be decreased if the individuals don’t respond to the pull requests, do the review, and push other work (task) in the done direction.&lt;/p&gt;

&lt;p&gt;If you are not in the middle of a focused task, you should do a code review shortly after it comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Code Review Improvements Over Time
&lt;/h2&gt;

&lt;p&gt;If you follow these guidelines and you are strict with your code reviews, you should find that the entire code review process tends to go faster and faster over time.&lt;/p&gt;

&lt;p&gt;Developers will learn what is required for healthy code, and when you create a pull request that is great from the start, it will require less and less review time.&lt;/p&gt;

&lt;p&gt;Reviewers will learn to respond quickly and not add unnecessary latency into the review process.&lt;/p&gt;

&lt;p&gt;But don’t compromise on the code review standards or quality for an imagined improvement in velocity—it’s not actually going to make anything happen more quickly, in the long run.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://google.github.io/eng-practices/"&gt;Google Engineering Practices Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.hypothes.is/blog/code-review-in-remote-teams/"&gt;Code review in remote teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mtlynch.io/human-code-reviews-1/"&gt;How to Do Code Reviews Like a Human (Part One)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mtlynch.io/human-code-reviews-2/"&gt;How to Do Code Reviews Like a Human (Part Two)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mtlynch.io/code-review-love/"&gt;How to Make Your Code Reviewer Fall in Love with You&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=11416746"&gt;How do you review code?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>development</category>
      <category>codereview</category>
    </item>
    <item>
      <title>REST API Integration with Feign HTTP client</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Sat, 26 Jun 2021 08:00:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/rest-api-integration-with-feign-http-client-17i7</link>
      <guid>https://dev.to/mkovacek/rest-api-integration-with-feign-http-client-17i7</guid>
      <description>&lt;h2&gt;
  
  
  1. &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In projects we often have some integrations with API layer of 3rd party systems. This means that on our side we need to prepare HTTP client to be able consume those endpoints. Based on API specification we need to prepare Resource representation classes. Then, HTTP client with some configurations, like headers, timeouts, error handling. Parse request and response payloads into objects and many other things. All off this on the end creates a lot of boilerplate code.&lt;/p&gt;

&lt;p&gt;Inspired with my latest project I want to introduce Feign HTTP client. Feign is HTTP client which goal is to simplify writing HTTP clients. Goal is to reduce complexity of REST API integrations.&lt;/p&gt;

&lt;p&gt;With Feign we need to declare and annotate an interface based on API specification. And in background Feign will process annotations into a templated requests. Feign offers you to configure different HTTP clients, JSON/XML processors, metric providers, loggers and some other features.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Example
&lt;/h2&gt;

&lt;p&gt;Throughout this article, we will integrate Faker APIs in Adobe Experience Manager (AEM) project. Goal is to get some fake generated data about users and addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Setup
&lt;/h2&gt;

&lt;p&gt;First, in your AEM project add needed dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- Feign --&amp;gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;io.github.openfeign&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;feign-core&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;11.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;io.github.openfeign&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;feign-jackson&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;11.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;!--Jackson --&amp;gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;com.fasterxml.jackson.core&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;jackson-core&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;2.12.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;com.fasterxml.jackson.core&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;jackson-annotations&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;2.12.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;com.fasterxml.jackson.core&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;jackson-databind&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;2.12.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides the &lt;strong&gt;&lt;em&gt;feign-core&lt;/em&gt;&lt;/strong&gt; , additionaly we'll use &lt;strong&gt;&lt;em&gt;feign-jackson&lt;/em&gt;&lt;/strong&gt; for JSON processing. _ &lt;strong&gt;Feign-jackson&lt;/strong&gt; _ requires &lt;strong&gt;&lt;em&gt;jackson-core&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;jackson-annotations&lt;/em&gt;&lt;/strong&gt; and _ &lt;strong&gt;jackson-databind&lt;/strong&gt; _ dependencies.&lt;/p&gt;

&lt;p&gt;To prevent unresolved import packages, we need to install those dependencies in &lt;strong&gt;&lt;em&gt;Apache Felix Web console&lt;/em&gt;&lt;/strong&gt; or embed them into our project bundle.&lt;/p&gt;

&lt;p&gt;Embed dependencies in _ &lt;strong&gt;filevault-package-maven-plugin&lt;/strong&gt; _ configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;embedded&amp;gt;
  &amp;lt;groupId&amp;gt;io.github.openfeign&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;feign-core&amp;lt;/artifactId&amp;gt;
  &amp;lt;target&amp;gt;/apps/aem-feign-vendor-packages/application/install&amp;lt;/target&amp;gt;
&amp;lt;/embedded&amp;gt;
&amp;lt;embedded&amp;gt;
  &amp;lt;groupId&amp;gt;io.github.openfeign&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;feign-jackson&amp;lt;/artifactId&amp;gt;
  &amp;lt;target&amp;gt;/apps/aem-feign-vendor-packages/application/install&amp;lt;/target&amp;gt;
&amp;lt;/embedded&amp;gt;
&amp;lt;embedded&amp;gt;
  &amp;lt;groupId&amp;gt;com.fasterxml.jackson.core&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;jackson-core&amp;lt;/artifactId&amp;gt;
  &amp;lt;target&amp;gt;/apps/aem-feign-vendor-packages/application/install&amp;lt;/target&amp;gt;
&amp;lt;/embedded&amp;gt;
&amp;lt;embedded&amp;gt;
  &amp;lt;groupId&amp;gt;com.fasterxml.jackson.core&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;jackson-annotations&amp;lt;/artifactId&amp;gt;
  &amp;lt;target&amp;gt;/apps/aem-feign-vendor-packages/application/install&amp;lt;/target&amp;gt;
&amp;lt;/embedded&amp;gt;
&amp;lt;embedded&amp;gt;
  &amp;lt;groupId&amp;gt;com.fasterxml.jackson.core&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;jackson-databind&amp;lt;/artifactId&amp;gt;
  &amp;lt;target&amp;gt;/apps/aem-feign-vendor-packages/application/install&amp;lt;/target&amp;gt;
&amp;lt;/embedded&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Feign HTTP client
&lt;/h2&gt;

&lt;p&gt;First of all we need to prepare Resource representation classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Data
public class Users {

    private String uuid;
    private String firstname;
    private String lastname;
    private String username;
    private String password;
    private String email;
    private String ip;
    private String macAddress;
    private String website;
    private String image;

}

@Data
public class Address {

    private String street;
    private String streetName;
    private String buildingNumber;
    private String city;
    private String zipcode;
    private String country;

    @JsonProperty("county_code")
    private String countyCode;

    private Double latitude;
    private Double longitude;

}

@Data
public class Response&amp;lt;T&amp;gt; {

    private String status;
    private int code;
    private long total;
    private List&amp;lt;T&amp;gt; data;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next step is to define FakerApi client which follows Faker endpoints specification. The &lt;strong&gt;&lt;em&gt;@RequestLine&lt;/em&gt;&lt;/strong&gt; annotation defines HTTP method and UriTemplate for request. Expressions wrapped in curly-braces are resolved using their corresponding &lt;strong&gt;&lt;em&gt;&lt;a class="mentioned-user" href="https://dev.to/param"&gt;@param&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt; annotated parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface FakerApi {

    @RequestLine("GET /users?_quantity={quantity}&amp;amp;_gender={gender}")
    Response&amp;lt;Users&amp;gt; users(@Param("quantity") long quantity, @Param("gender") String gender);

    @RequestLine("GET /addresses?_quantity={quantity}")
    Response&amp;lt;Address&amp;gt; addresses(@Param("quantity") long quantity);

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last thing is to create FakerApi client and that's all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FakerApi fakerApi = Feign.builder()
                     .decoder(new JacksonDecoder())
                     .target(FakerApi.class, "https://fakerapi.it/api/v1");

Response&amp;lt;Address&amp;gt; address = fakerApi.addresses(2);
Response&amp;lt;Users&amp;gt; femaleUsers = fakerApi.users(2, "female");                     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;5. Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this article, we've explained how to simplify REST API Integrations in AEM with Feign HTTP client.&lt;/p&gt;

&lt;p&gt;All code samples are available on &lt;a href="https://github.com/mkovacek/aem-feign"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more details about Feign check official &lt;a href="https://github.com/OpenFeign/feign"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>feign</category>
      <category>rest</category>
      <category>aem</category>
    </item>
    <item>
      <title>Speed up the AEM Build Time</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Wed, 30 Dec 2020 12:13:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/speed-up-the-aem-build-time-3bmo</link>
      <guid>https://dev.to/mkovacek/speed-up-the-aem-build-time-3bmo</guid>
      <description>&lt;h2&gt;
  
  
  How to Speed up the AEM Build Time
&lt;/h2&gt;

&lt;p&gt;Recently I found out tool to speed up build time of your Maven based projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maven Daemon&lt;/strong&gt; is let's say maven wrapper tool which provides faster build time.&lt;/p&gt;

&lt;p&gt;Some facts about Maven Daemon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build executes in long living background process&lt;/li&gt;
&lt;li&gt;native executable &lt;/li&gt;
&lt;li&gt;use multiple CPU cores to build modules in parallel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More information about Maven Daemon you can find in &lt;a href="https://github.com/mvndaemon/mvnd"&gt;github repository&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Maven Daemon Installation
&lt;/h4&gt;

&lt;p&gt;Use &lt;strong&gt;sdk&lt;/strong&gt; or &lt;strong&gt;brew&lt;/strong&gt; to install Maven Daemon&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sdk install mvnd

$ brew install mvndaemon/homebrew-mvnd/mvnd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify installation with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ ~ mvnd --version
mvnd native client 0.2.0-darwin-amd64 (0cd0b3f04692b7970fda06c206c1fbaac68fe9ce)
Terminal: org.jline.terminal.impl.PosixSysTerminal with pty org.jline.terminal.impl.jansi.osx.OsXNativePty
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /usr/local/Cellar/mvnd/HEAD-1c610c3/libexec/mvn
Java version: 15.0.1, vendor: N/A, runtime: /usr/local/Cellar/openjdk/15.0.1/libexec/openjdk.jdk/Contents/Home
Default locale: en_GB, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Maven vs Maven Daemon build time comparison
&lt;/h4&gt;

&lt;p&gt;For testing purposes I will compare build times for AEM project. Project was build 2x times before measurement.&lt;/p&gt;

&lt;p&gt;This measurement is not perfect, since a lot of factors can affect final build time (+- few seconds), but is fair enough to see a difference.&lt;/p&gt;

&lt;p&gt;For testing purposes I have used AEM Project Archtype version 24.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;mvn clean install&lt;/strong&gt; vs &lt;strong&gt;mvnd clean install&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;mvn: 26.586 s&lt;/li&gt;
&lt;li&gt;mvnd: 12.654 s
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn clean install

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.242 s]
[INFO] My Site - Core ..................................... SUCCESS [6.994 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [11.099 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.795 s]
[INFO] My Site - UI apps .................................. SUCCESS [3.890 s]
[INFO] My Site - UI content ............................... SUCCESS [1.672 s]
[INFO] My Site - UI config ................................ SUCCESS [0.105 s]
[INFO] My Site - All ...................................... SUCCESS [0.138 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.080 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.274 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 26.586 s
[INFO] Finished at: 2020-12-30T11:59:11+01:00
[INFO] ------------------------------------------------------------------------

mvnd clean install

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.016 s]
[INFO] My Site - Core ..................................... SUCCESS [4.300 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [10.442 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.178 s]
[INFO] My Site - UI apps .................................. SUCCESS [1.126 s]
[INFO] My Site - UI content ............................... SUCCESS [0.911 s]
[INFO] My Site - UI config ................................ SUCCESS [0.156 s]
[INFO] My Site - All ...................................... SUCCESS [0.107 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.078 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.063 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.654 s (Wall Clock)
[INFO] Finished at: 2020-12-30T11:59:58+01:00
[INFO] ------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;mvn clean install&lt;/strong&gt;  &lt;strong&gt;-PautoInstallPackage&lt;/strong&gt; vs &lt;strong&gt;mvnd clean install -PautoInstallPackage&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;mvn: 29.582 s&lt;/li&gt;
&lt;li&gt;mvnd: 12.995 s
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn clean install -PautoInstallPackage

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.223 s]
[INFO] My Site - Core ..................................... SUCCESS [7.354 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [11.338 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.826 s]
[INFO] My Site - UI apps .................................. SUCCESS [4.343 s]
[INFO] My Site - UI content ............................... SUCCESS [2.885 s]
[INFO] My Site - UI config ................................ SUCCESS [0.454 s]
[INFO] My Site - All ...................................... SUCCESS [0.346 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.061 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.374 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 29.582 s
[INFO] Finished at: 2020-12-30T12:06:29+01:00
[INFO] ------------------------------------------------------------------------

mvnd clean install -PautoInstallPackage

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.005 s]
[INFO] My Site - Core ..................................... SUCCESS [4.004 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [10.288 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.177 s]
[INFO] My Site - UI apps .................................. SUCCESS [1.433 s]
[INFO] My Site - UI content ............................... SUCCESS [1.010 s]
[INFO] My Site - UI config ................................ SUCCESS [0.292 s]
[INFO] My Site - All ...................................... SUCCESS [0.227 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.079 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.053 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.995 s (Wall Clock)
[INFO] Finished at: 2020-12-30T12:08:30+01:00
[INFO] ------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your AEM project build will fail with Maven Daemon if you use &lt;strong&gt;bnd-maven-plugin&lt;/strong&gt; version 5.1.0&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ERROR] Failed to execute goal biz.aQute.bnd:bnd-maven-plugin:5.0.0:bnd-process (bnd-process) on project lot-polish-airlines.core: bnd error: null: ConcurrentModificationException -&amp;gt; [Help 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see by results Maven Daemon build time is approximately 2x faster then with normal maven. Installation was pretty much straightforward and so far didn't notice any downsides, so why not use it and save some time.&lt;/p&gt;

</description>
      <category>aem</category>
      <category>maven</category>
    </item>
    <item>
      <title>Speed up the Maven Build Time</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Tue, 29 Dec 2020 23:00:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/speed-up-the-maven-build-time-4odd</link>
      <guid>https://dev.to/mkovacek/speed-up-the-maven-build-time-4odd</guid>
      <description>&lt;h2&gt;
  
  
  How to Speed up the Maven Build Time
&lt;/h2&gt;

&lt;p&gt;Recently I found out tool to speed up build time of your Maven based projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maven Daemon&lt;/strong&gt; is let's say maven wrapper which provides faster build time.&lt;/p&gt;

&lt;p&gt;Some facts about Maven Daemon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build executes in long living background process&lt;/li&gt;
&lt;li&gt;native executable &lt;/li&gt;
&lt;li&gt;use multiple CPU cores to build modules in parallel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More information about Maven Daemon you can find in &lt;a href="https://github.com/mvndaemon/mvnd"&gt;github repository&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Maven Daemon Installation
&lt;/h4&gt;

&lt;p&gt;Use &lt;strong&gt;sdk&lt;/strong&gt; or &lt;strong&gt;brew&lt;/strong&gt; to install Maven Daemon&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sdk install mvnd

$ brew install mvndaemon/homebrew-mvnd/mvnd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify installation with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ ~ mvnd --version
mvnd native client 0.2.0-darwin-amd64 (0cd0b3f04692b7970fda06c206c1fbaac68fe9ce)
Terminal: org.jline.terminal.impl.PosixSysTerminal with pty org.jline.terminal.impl.jansi.osx.OsXNativePty
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /usr/local/Cellar/mvnd/HEAD-1c610c3/libexec/mvn
Java version: 15.0.1, vendor: N/A, runtime: /usr/local/Cellar/openjdk/15.0.1/libexec/openjdk.jdk/Contents/Home
Default locale: en_GB, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Maven vs Maven Daemon build time comparison
&lt;/h4&gt;

&lt;p&gt;For testing purposes I will compare build times for AEM project. Project was build 2x times before measurement.&lt;/p&gt;

&lt;p&gt;This measurement is not perfect, since a lot of factors can affect final build time (+- few seconds), but it's fair enough to see a difference.&lt;/p&gt;

&lt;p&gt;For testing purposes I have used AEM Project Archtype version 24.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;mvn clean install&lt;/strong&gt; vs &lt;strong&gt;mvnd clean install&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;mvn: 26.586 s&lt;/li&gt;
&lt;li&gt;mvnd: 12.654 s
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn clean install

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.242 s]
[INFO] My Site - Core ..................................... SUCCESS [6.994 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [11.099 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.795 s]
[INFO] My Site - UI apps .................................. SUCCESS [3.890 s]
[INFO] My Site - UI content ............................... SUCCESS [1.672 s]
[INFO] My Site - UI config ................................ SUCCESS [0.105 s]
[INFO] My Site - All ...................................... SUCCESS [0.138 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.080 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.274 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 26.586 s
[INFO] Finished at: 2020-12-30T11:59:11+01:00
[INFO] ------------------------------------------------------------------------

mvnd clean install

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.016 s]
[INFO] My Site - Core ..................................... SUCCESS [4.300 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [10.442 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.178 s]
[INFO] My Site - UI apps .................................. SUCCESS [1.126 s]
[INFO] My Site - UI content ............................... SUCCESS [0.911 s]
[INFO] My Site - UI config ................................ SUCCESS [0.156 s]
[INFO] My Site - All ...................................... SUCCESS [0.107 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.078 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.063 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.654 s (Wall Clock)
[INFO] Finished at: 2020-12-30T11:59:58+01:00
[INFO] ------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;mvn clean install&lt;/strong&gt;  &lt;strong&gt;-PautoInstallPackage&lt;/strong&gt; vs &lt;strong&gt;mvnd clean install -PautoInstallPackage&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;mvn: 29.582 s&lt;/li&gt;
&lt;li&gt;mvnd: 12.995 s
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn clean install -PautoInstallPackage

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.223 s]
[INFO] My Site - Core ..................................... SUCCESS [7.354 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [11.338 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.826 s]
[INFO] My Site - UI apps .................................. SUCCESS [4.343 s]
[INFO] My Site - UI content ............................... SUCCESS [2.885 s]
[INFO] My Site - UI config ................................ SUCCESS [0.454 s]
[INFO] My Site - All ...................................... SUCCESS [0.346 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.061 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.374 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 29.582 s
[INFO] Finished at: 2020-12-30T12:06:29+01:00
[INFO] ------------------------------------------------------------------------

mvnd clean install -PautoInstallPackage

[INFO] Reactor Summary for mysite 1.0.0-SNAPSHOT:
[INFO]
[INFO] mysite ............................................. SUCCESS [0.005 s]
[INFO] My Site - Core ..................................... SUCCESS [4.004 s]
[INFO] My Site - UI Frontend .............................. SUCCESS [10.288 s]
[INFO] My Site - Repository Structure Package ............. SUCCESS [0.177 s]
[INFO] My Site - UI apps .................................. SUCCESS [1.433 s]
[INFO] My Site - UI content ............................... SUCCESS [1.010 s]
[INFO] My Site - UI config ................................ SUCCESS [0.292 s]
[INFO] My Site - All ...................................... SUCCESS [0.227 s]
[INFO] My Site - Dispatcher ............................... SUCCESS [0.079 s]
[INFO] My Site - UI Tests ................................. SUCCESS [0.053 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.995 s (Wall Clock)
[INFO] Finished at: 2020-12-30T12:08:30+01:00
[INFO] ------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your AEM project build will fail with Maven Daemon if you use &lt;strong&gt;bnd-maven-plugin&lt;/strong&gt; version older than 5.1.0&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ERROR] Failed to execute goal biz.aQute.bnd:bnd-maven-plugin:5.0.0:bnd-process (bnd-process) on project my-site.core: bnd error: null: ConcurrentModificationException -&amp;gt; [Help 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Sum up
&lt;/h4&gt;

&lt;p&gt;As test results shows, Maven Daemon build time is approximately 2x faster then normal maven. Installation is pretty much straightforward and so far didn't notice any downsides, so why not use it and save some time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;Not 100% sure, but I think installation of Maven Daemon has override my maven configuration (settings.xml).&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Test behaviour, not implementation</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Sat, 20 Jun 2020 09:30:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/test-behaviour-not-implementation-3g2j</link>
      <guid>https://dev.to/mkovacek/test-behaviour-not-implementation-3g2j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Last year I spent a lot of time with writing and also practicing how to write good unit/integration tests in AEM (Adobe Experience Manager). Now I would like to share with you what I have learned so far. What I have learned is not only AEM related, you can apply it to any programming language or framework.&lt;/p&gt;

&lt;p&gt;Before that time I had some "experience" with unit testing. I wrote several unit tests so that I can say "I have experience with it". But to be honest I didn't like to write it.&lt;/p&gt;

&lt;p&gt;Even though I knew what benefits tests brings to the product what we are building, what brings to the team members and to me, I didn't care. Typical excuses were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I don't have time or we don't have time for it"&lt;/li&gt;
&lt;li&gt;"I can't test or mock that"&lt;/li&gt;
&lt;li&gt;"You can't write test, pick up some new feature or fix bug" &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and on the end nobody pushed me to write it. Sadly, writing tests wasn't part of development process.&lt;/p&gt;

&lt;p&gt;Now when I'm thinking a little bit, I didn't know how to write tests. Let's face it, writing tests is not easy, like many other things when you don't have experience with it.&lt;/p&gt;

&lt;p&gt;Luckily that kind of things has changed at some point, and I would like to try convince all of you who are still thinking like "old" me.&lt;/p&gt;

&lt;p&gt;I would like that we all start thinking more about Quality and not Quantity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing tests as part of development process
&lt;/h2&gt;

&lt;p&gt;Most of us works in "Agile" way (whatever that means) and use DDD (Deadline-driven development) methodology (I could write about that in separate post). This usually means that there is no time for writing tests. This need's to be changed, developers and all other technical team members should convince all other team members that writing tests should be part of development process. Writing tests should be part of any estimation. Period. Why?&lt;/p&gt;

&lt;p&gt;There are a lot of benefits, but I will point out most important of them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bugs prevention&lt;/li&gt;
&lt;li&gt;Better code quality&lt;/li&gt;
&lt;li&gt;Provides some kind of documentation&lt;/li&gt;
&lt;li&gt;Time saving&lt;/li&gt;
&lt;li&gt;Money saving&lt;/li&gt;
&lt;li&gt;Feeling "safe"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now let's see typical "disadvantages":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Time consuming&lt;/li&gt;
&lt;li&gt;Money consuming&lt;/li&gt;
&lt;li&gt;Tests are slow to write&lt;/li&gt;
&lt;li&gt;Tests are slow to run&lt;/li&gt;
&lt;li&gt;Changing implementation require changing tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Probably you have notice that I have mention &lt;strong&gt;time and money&lt;/strong&gt; as advantage and disadvantage. Depending if you are thinking in short term, then yes it's waist of time and money, but If you think in long term then it's not true. Actually on the end it saves your time and money.&lt;/p&gt;

&lt;p&gt;A lot of people thinks that they are waist of time and money. I think because they don't see some visual outcome of them, like how they see it when you build some feature. Try to think like this, with tests you can prevent a lot of bugs and a lot of ping pongs between Developers and QAs. Very often we have change requests during development and it happens that we implemented something in wrong way. New request just doesn't fit anymore to existing implementation. That means we need to refactor our old code or reimplement it from scratch. Here tests provides you some safe feel because you know if you broke behaviour or not. Another example could be big, never ending projects where several different teams have worked before you. Probably that project has poorly written documentation, you need to deal with legacy code and implement new features on top of it. Having tests is gold here. Also, a lot of projects starts like MVP which turns out to some core / base project with several subprojects. Not having test coverage here is total nonsense.&lt;/p&gt;

&lt;p&gt;Last 3 disadvantages are also not true.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests are slow to write

&lt;ul&gt;
&lt;li&gt;yes, If you don't know how to write it and if you don't have experience&lt;/li&gt;
&lt;li&gt;practice&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tests are slow to run

&lt;ul&gt;
&lt;li&gt;again yes, if you don't know how to write it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Changing implementation require changing tests

&lt;ul&gt;
&lt;li&gt;yes, because you are testing wrong things&lt;/li&gt;
&lt;li&gt;test behaviour not implementation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't believe me?&lt;/p&gt;

&lt;p&gt;Take 1h of your time and watch the talk &lt;strong&gt;"TDD, Where Did It All Go Wrong"&lt;/strong&gt; from &lt;strong&gt;Ian Cooper.&lt;/strong&gt; For me this was eye opener. Before this talk I read few books about testing and I was not so convinced. In my opinion this is definitely the best talk about it.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/EZ05e7EMOLM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl; dr;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test behaviour / requirements, not implementation&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;with this kind of approach you will eliminate previously mentioned disadvantages&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Test the public API of a module, not classes, methods or technical details&lt;/li&gt;
&lt;li&gt;Unit test shouldn't be focused on classes, methods it should be focused on module, user stories&lt;/li&gt;
&lt;li&gt;Test gives you promise what should be expected result / behaviour, so when you are refactoring an implementation, use tests to make sure that the implementation still yields the expected results&lt;/li&gt;
&lt;li&gt;Write tests to cover the use cases or stories&lt;/li&gt;
&lt;li&gt;Use the "Given When Then" model&lt;/li&gt;
&lt;li&gt;Avoid mocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This testing approach helps you to build &lt;strong&gt;right product&lt;/strong&gt;. But negative point could be that it doesn't help you to build &lt;strong&gt;product right&lt;/strong&gt;. Other downside is that you don't see exactly what is wrong when test is failing.&lt;/p&gt;

&lt;p&gt;So classic unit testing approach push you to write more clean and quality code than "behaviour testing". In my opinion strict code reviews and static code analysis tools are better approach to achieve the same result. Second downside for me is really minor thing, since with debugging you can quickly find out what is happening.&lt;/p&gt;

&lt;p&gt;I hope that you are still follow me and that I'm start changing a little bit your thinking about testing.&lt;/p&gt;

&lt;p&gt;Now let's stop with theory and let's see how it works in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing in AEM
&lt;/h2&gt;

&lt;p&gt;Because last few years I'm working with AEM, I will show you how to test behaviours in your AEM projects. The same things you can apply in any other programming languages or frameworks. Depending on testing library support, this can be easier or harder to achieve.&lt;/p&gt;

&lt;p&gt;As an example let say we need to implement Product Details API which is consumed by client side. To build Product Details API lets say in Spring you will probably create several classes like Product Controller, Service, Repository, DTO and so on. In AEM world this means you need to create Sling Servlet, OSGi Service, Sling Model and some DTO classes.&lt;/p&gt;

&lt;p&gt;Product Details acceptance criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;show product details information (id, name, description, category id, images and variants)&lt;/li&gt;
&lt;li&gt;product variants need's to be available for specific country&lt;/li&gt;
&lt;li&gt;product variants are available from specific date&lt;/li&gt;
&lt;li&gt;product variants need's to be sorted by sort order&lt;/li&gt;
&lt;li&gt;name and description of product need's to be localized (depending on market), fallback is English&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementation what you will see here is not perfect, it's simplified and hardcoded. In real world this is more complex. But here implementation is not important, instead we should focus how to test requirements of this API.&lt;/p&gt;

&lt;p&gt;I will add here just 3 most important classes, other implementations you can see on &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/servlets/products/ProductDetailsServlet.java"&gt;ProductDetails Sling Servlet&lt;/a&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;for handling request&lt;/li&gt;
&lt;li&gt;it does some request validation&lt;/li&gt;
&lt;li&gt;it use ProductDetailsService to get all information about requested product
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.mkovacek.aem.core.servlets.products;

import com.mkovacek.aem.core.models.products.ProductDetailsModel;
import com.mkovacek.aem.core.records.response.Response;
import com.mkovacek.aem.core.services.products.ProductDetailsService;
import com.mkovacek.aem.core.services.response.ResponseService;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

import java.io.IOException;

@Slf4j
@Component(service = Servlet.class)
@SlingServletResourceTypes(
    resourceTypes = ProductDetailsServlet.RESOURCE_TYPE,
    selectors = ProductDetailsServlet.ALLOWED_SELECTOR,
    extensions = ProductDetailsServlet.JSON,
    methods = HttpConstants.METHOD_GET)
public class ProductDetailsServlet extends SlingSafeMethodsServlet {

    public static final String ALLOWED_SELECTOR = "productdetails";
    static final String RESOURCE_TYPE = "demo/components/productdetails";
    static final String JSON = "json";

    @Reference
    private transient ResponseService responseService;

    @Reference
    private transient ProductDetailsService productDetailsService;

    @Override
    public void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException, IOException {
        try {
            this.responseService.setJsonContentType(response);
            final String selector = request.getRequestPathInfo().getSelectorString();
            final String productId = this.responseService.getSuffix(request);

            if (this.responseService.areSelectorsValid(selector, ALLOWED_SELECTOR) &amp;amp;&amp;amp; StringUtils.isNotBlank(productId)) {
                final Resource resource = request.getResource();
                final Response&amp;lt;ProductDetailsModel&amp;gt; data = this.productDetailsService.getProductDetails(productId, resource);
                this.responseService.sendOk(response, data);
            } else {
                this.responseService.sendBadRequest(response);
            }
        } catch (final Exception e) {
            log.error("Exception during handling request", e);
            this.responseService.sendInternalServerError(response);
        }
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/services/products/impl/ProductDetailsServiceImpl.java"&gt;ProductDetails OSGi Service&lt;/a&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;it's searching for requested product in repository / database&lt;/li&gt;
&lt;li&gt;it's doing some product validation&lt;/li&gt;
&lt;li&gt;maps product resource to ProductDetails model&lt;/li&gt;
&lt;li&gt;returns product details
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.mkovacek.aem.core.services.products.impl;

import com.day.cq.wcm.api.PageManager;
import com.mkovacek.aem.core.models.products.ProductDetailsModel;
import com.mkovacek.aem.core.records.response.Response;
import com.mkovacek.aem.core.records.response.Status;
import com.mkovacek.aem.core.services.products.ProductDetailsService;
import com.mkovacek.aem.core.services.resourceresolver.ResourceResolverService;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import java.util.Locale;
import java.util.Optional;

@Slf4j
@Component(service = ProductDetailsService.class, immediate = true)
public class ProductDetailsServiceImpl implements ProductDetailsService {

    private static final String PIM_READER = "pimReader";
    private static final Response&amp;lt;ProductDetailsModel&amp;gt; notFoundResponse = new Response&amp;lt;&amp;gt;(new Status(true, "Product Details not found"), null);
    private static final Response&amp;lt;ProductDetailsModel&amp;gt; errorResponse = new Response&amp;lt;&amp;gt;(new Status(false, "Error during fetching product details"), null);

    @Reference
    private ResourceResolverService resourceResolverService;

    @Override
    public Response&amp;lt;ProductDetailsModel&amp;gt; getProductDetails(final String id, final Resource resource) {
        try (final ResourceResolver resourceResolver = this.resourceResolverService.getResourceResolver(PIM_READER)) {
            final Locale locale = resourceResolver.adaptTo(PageManager.class).getContainingPage(resource).getLanguage(false);
            //usually this would be implemented with query
            final String productPath = StringUtils.join("/var/commerce/products/demo/", id);
            return Optional.ofNullable(resourceResolver.getResource(productPath))
                       .map(productResource -&amp;gt; productResource.adaptTo(ProductDetailsModel.class))
                       .map(productDetailsModel -&amp;gt; productDetailsModel.setLocale(locale))
                       .filter(ProductDetailsModel::isValid)
                       .map(productDetailsModel -&amp;gt; new Response&amp;lt;&amp;gt;(new Status(true), productDetailsModel))
                       .orElse(notFoundResponse);
        } catch (final Exception e) {
            log.error("Exception during fetching product details", e);
        }
        return errorResponse;
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/models/products/ProductDetailsModel.java"&gt;ProductDetails Sling Model&lt;/a&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;representation of product resource in repository / database&lt;/li&gt;
&lt;li&gt;used as response in JSON format
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.mkovacek.aem.core.models.products;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.mkovacek.aem.core.services.products.ProductLocalizationService;
import com.mkovacek.aem.core.services.products.ProductValidatorService;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

@Slf4j
@Model(adaptables = {Resource.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ProductDetailsModel {

    @ValueMapValue
    @Default(values = StringUtils.EMPTY)
    @Getter
    private String id;

    @ValueMapValue
    @Default(values = StringUtils.EMPTY)
    @Getter
    private String categoryId;

    @ChildResource
    @Getter
    private List&amp;lt;ImageModel&amp;gt; images;

    @ChildResource
    private List&amp;lt;VariantsModel&amp;gt; variants;

    @Self
    private ValueMap valueMap;

    @OSGiService
    private ProductLocalizationService productLocalizationService;

    @OSGiService
    private ProductValidatorService productValidatorService;

    @Getter
    @JsonProperty("variants")
    private List&amp;lt;VariantsModel&amp;gt; validVariants = new ArrayList&amp;lt;&amp;gt;();

    @Getter
    private String name = StringUtils.EMPTY;

    @Getter
    private String description = StringUtils.EMPTY;

    @JsonIgnore
    public boolean isValid() {
        return !this.validVariants.isEmpty();
    }

    @JsonIgnore
    public ProductDetailsModel setLocale(final Locale locale) {
        this.setLocalizedValues(locale);
        this.validateAndSortVariants(locale);
        return this;
    }

    private void setLocalizedValues(final Locale locale) {
        this.name = this.productLocalizationService.getLocalizedProductDetail(this.valueMap, "name.", locale);
        this.description = this.productLocalizationService.getLocalizedProductDetail(this.valueMap, "description.", locale);
    }

    private void validateAndSortVariants(final Locale locale) {
        this.validVariants = this.productValidatorService.getValidVariants(this.variants, locale);
        this.validVariants.sort(Comparator.comparing(VariantsModel::getSortOrder));
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except those 3 classes I need to create several more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/models/products/ImageModel.java"&gt;ImageModel&lt;/a&gt;, &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/models/products/VariantsModel.java"&gt;VariantsModel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/services/blobstorage/impl/BlobStorageServiceImpl.java"&gt;BlobStorageService&lt;/a&gt;, &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/services/products/impl/ProductValidatorServiceImpl.java"&gt;ProductValidatorService&lt;/a&gt;, &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/services/products/impl/ProductLocalizationServiceImpl.java"&gt;ProductLocalizationService&lt;/a&gt;, &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/services/resourceresolver/impl/ResourceResolverServiceImpl.java"&gt;ResourceResolverService&lt;/a&gt;, &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/services/response/impl/ResponseServiceImpl.java"&gt;ResponseService&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/records/response/Response.java"&gt;Response&lt;/a&gt; and &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/main/java/com/mkovacek/aem/core/records/response/Status.java"&gt;Status&lt;/a&gt; records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You saw that we have a lot of classes to build this user story. Usually what would developer test here are OSGi services. I'm not saying this is a bad approach, but for that you will need more time, and every time when you will refactor your code or add some new stuff, it's very likely that you will need to change your tests as well.&lt;/p&gt;

&lt;p&gt;Instead of that let's test only Servlet because this is public API of this user story. So what we need to test in Servlet? First of all, we need to cover all requirments from acceptance criteria, additionaly we can cover some technical details of servlet implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test libraries in AEM
&lt;/h2&gt;

&lt;p&gt;At the moment in my opinion the best library what you can use are &lt;a href="https://wcm.io/testing/aem-mock/"&gt;AEM Mocks&lt;/a&gt;. AEM Mocks supports most common mock implementations of AEM APIs + contains Apache Sling and OSGi mock implementations. For other not implemented mocks you will need to implement it by yourself or use &lt;a href="https://site.mockito.org"&gt;Mockito&lt;/a&gt;. Besides those two I will use Junit 5.&lt;/p&gt;

&lt;p&gt;Some tips before we start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try to have Test classes clean as possible, it should contains just tests. &lt;/li&gt;
&lt;li&gt;Move mocks in separate classes&lt;/li&gt;
&lt;li&gt;Create some Util classes with common helper methods, if you repeat yourself in multiple places&lt;/li&gt;
&lt;li&gt;Use @BeforeAll / AfterAll, @BeforeEach / AfterEach, Junit 5 annotations to not repeat yourself in every test method and to speed up your tests&lt;/li&gt;
&lt;li&gt;Create common AEM context in separate class if you repeat yourself in several test classes&lt;/li&gt;
&lt;li&gt;Don't programmatically create complex resources in AEM context, instead export it from real AEM instance as JSON resource and load it into AEM context.&lt;/li&gt;
&lt;li&gt;Use ResourceResolverMock type whenever is possible to speed up your tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/test/java/com/mkovacek/aem/core/servlets/products/ProductDetailsServletTest.java"&gt;ProductDetailsServletTest&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;You will see that this test class is more or less clean and it focused only on tests. There is no mocking here, separated mock example you can see &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/test/java/com/mkovacek/aem/core/context/mocks/MockExternalizer.java"&gt;here&lt;/a&gt;. I'm using @BeforeAll and @BeforeEach to do some common setup, like setting up market pages/resources and common request informations. Also I needed some helper class to easier register all necessary classes into &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/blob/develop/core/src/test/java/com/mkovacek/aem/core/context/AppAemContextBuilder.java"&gt;AEM context&lt;/a&gt;. All resources are exported as JSON from real AEM instance and imported into AEM context so that we test on &lt;a href="https://github.com/mkovacek/how-to-test-aem-demo/tree/develop/core/src/test/resources/jcr_root"&gt;real data&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this test class I'm testing technical details and requirements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;technical details

&lt;ul&gt;
&lt;li&gt;request validation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;requirements

&lt;ul&gt;
&lt;li&gt;response for non existing product id&lt;/li&gt;
&lt;li&gt;product details in different markets to cover localization&lt;/li&gt;
&lt;li&gt;product variants validation for specific markets &lt;/li&gt;
&lt;li&gt;product variants availability from specific date&lt;/li&gt;
&lt;li&gt;product varinats sorting
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.mkovacek.aem.core.servlets.products;

import com.day.cq.wcm.api.Page;
import com.mkovacek.aem.core.context.AppAemContextBuilder;
import com.mkovacek.aem.core.context.constants.TestConstants;
import com.mkovacek.aem.core.context.utils.ResourceUtil;
import com.mkovacek.aem.core.services.blobstorage.impl.BlobStorageServiceImpl;
import com.mkovacek.aem.core.services.products.impl.ProductDetailsServiceImpl;
import com.mkovacek.aem.core.services.products.impl.ProductLocalizationServiceImpl;
import com.mkovacek.aem.core.services.products.impl.ProductValidatorServiceImpl;
import com.mkovacek.aem.core.services.resourceresolver.impl.ResourceResolverServiceImpl;
import com.mkovacek.aem.core.services.response.impl.ResponseServiceImpl;

import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.testing.mock.sling.servlet.MockRequestPathInfo;
import org.apache.sling.testing.resourceresolver.MockResourceResolverFactory;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith(AemContextExtension.class)
class ProductDetailsServletTest {

    private static final AemContext context = new AppAemContextBuilder()
                                                  .loadResource(TestConstants.HR_HR_LANDING_PAGE_JSON, TestConstants.HR_HR_LANDING_PAGE_PATH)
                                                  .loadResource(TestConstants.DE_AT_LANDING_PAGE_JSON, TestConstants.DE_AT_LANDING_PAGE_PATH)
                                                  .loadResource(TestConstants.FR_FR_LANDING_PAGE_JSON, TestConstants.FR_FR_LANDING_PAGE_PATH)
                                                  .loadResource(TestConstants.PRODUCTS_JSON, TestConstants.PRODUCTS_PATH)
                                                  .registerService(ResourceResolverFactory.class, new MockResourceResolverFactory())
                                                  .registerInjectActivateService(new ResourceResolverServiceImpl())
                                                  .registerInjectActivateService(new ResponseServiceImpl())
                                                  .registerInjectActivateService(new BlobStorageServiceImpl(), Collections.singletonMap("productImagesFolderPath", "https://dummyurl.com/images/products/"))
                                                  .registerInjectActivateService(new ProductValidatorServiceImpl())
                                                  .registerInjectActivateService(new ProductLocalizationServiceImpl())
                                                  .registerInjectActivateService(new ResponseServiceImpl())
                                                  .registerInjectActivateService(new ProductDetailsServiceImpl())
                                                  .build();

    private static final MockRequestPathInfo requestPathInfo = context.requestPathInfo();
    private final ProductDetailsServlet servlet = context.registerInjectActivateService(new ProductDetailsServlet());
    private static final String CONTENT_RESOURCE_PATH = "root/productdetails";
    private static String NOT_FOUND_RESPONSE;
    private static String BAD_REQUEST_RESPONSE;

    @BeforeAll
    static void setUpBeforeAllTests() throws IOException {
        context.addModelsForPackage(TestConstants.SLING_MODELS_PACKAGES);
        requestPathInfo.setExtension("json");
        NOT_FOUND_RESPONSE = ResourceUtil.getExpectedResult(ProductDetailsServlet.class, "responses/not-found-response.json");
        BAD_REQUEST_RESPONSE = ResourceUtil.getExpectedResult(ProductDetailsServlet.class, "responses/bad-request-response.json");
    }

    @BeforeEach
    void setupBeforeEachTest() {
        context.response().resetBuffer();
        requestPathInfo.setSelectorString(ProductDetailsServlet.ALLOWED_SELECTOR);
        requestPathInfo.setSuffix("123456789");
        final Page page = context.pageManager().getPage(TestConstants.HR_HR_LANDING_PAGE_PATH);
        context.request().setResource(page.getContentResource(CONTENT_RESOURCE_PATH));
    }

    @Test
    @DisplayName("GIVEN landing page (en-HR) WHEN servlet is called with not valid selector THEN it returns bad request response in JSON format")
    void testNotValidSelector() throws ServletException, IOException {
        requestPathInfo.setSelectorString(ProductDetailsServlet.ALLOWED_SELECTOR + ".test");
        this.servlet.doGet(context.request(), context.response());

        assertAll(
            () -&amp;gt; assertEquals(HttpServletResponse.SC_BAD_REQUEST, context.response().getStatus()),
            () -&amp;gt; assertEquals(BAD_REQUEST_RESPONSE, context.response().getOutputAsString())
        );
    }

    @Test
    @DisplayName("GIVEN landing page (en-HR) WHEN servlet is called without productId suffix THEN it returns bad request response in JSON format")
    void testNoProductId() throws ServletException, IOException {
        requestPathInfo.setSuffix(StringUtils.EMPTY);
        this.servlet.doGet(context.request(), context.response());

        assertAll(
            () -&amp;gt; assertEquals(HttpServletResponse.SC_BAD_REQUEST, context.response().getStatus()),
            () -&amp;gt; assertEquals(BAD_REQUEST_RESPONSE, context.response().getOutputAsString())
        );
    }

    @Test
    @DisplayName("GIVEN landing page (en-HR) WHEN servlet is called with not existing productId THEN it returns not found response in JSON format")
    void testNotExistingProductId() throws ServletException, IOException {
        requestPathInfo.setSuffix("123abc");
        this.servlet.doGet(context.request(), context.response());

        assertAll(
            () -&amp;gt; assertEquals(HttpServletResponse.SC_OK, context.response().getStatus()),
            () -&amp;gt; assertEquals(NOT_FOUND_RESPONSE, context.response().getOutputAsString())
        );
    }

    @Test
    @DisplayName("GIVEN landing page (en-HR) WHEN servlet is called with existing productId THEN it returns an expected localized (fallback) product details response in JSON format")
    void testProductDetailsInCroatianMarket() throws ServletException, IOException {
        this.servlet.doGet(context.request(), context.response());
        final String expectedProductDetails = ResourceUtil.getExpectedResult(this.getClass(), "responses/product-123456789-hr-HR.json");

        assertAll(
            () -&amp;gt; assertEquals(HttpServletResponse.SC_OK, context.response().getStatus()),
            () -&amp;gt; assertEquals(expectedProductDetails, context.response().getOutputAsString())
        );
    }

    @Test
    @DisplayName("GIVEN landing page (de-AT) WHEN servlet is called with existing productId THEN it returns an expected localized product details response in JSON format")
    void testProductDetailsInAustrianMarket() throws ServletException, IOException {
        this.setPageResource(TestConstants.DE_AT_LANDING_PAGE_PATH);
        this.servlet.doGet(context.request(), context.response());
        final String expectedProductDetails = ResourceUtil.getExpectedResult(this.getClass(), "responses/product-123456789-at-DE.json");

        assertAll(
            () -&amp;gt; assertEquals(HttpServletResponse.SC_OK, context.response().getStatus()),
            () -&amp;gt; assertEquals(expectedProductDetails, context.response().getOutputAsString())
        );
    }

    @Test
    @DisplayName("GIVEN landing page (fr-FR) WHEN servlet is called with existing productId which is not valid for French market THEN it returns not found response in JSON format")
    void testProductDetailsInFrenchMarket() throws ServletException, IOException {
        this.setPageResource(TestConstants.FR_FR_LANDING_PAGE_PATH);
        this.servlet.doGet(context.request(), context.response());

        assertAll(
            () -&amp;gt; assertEquals(HttpServletResponse.SC_OK, context.response().getStatus()),
            () -&amp;gt; assertEquals(NOT_FOUND_RESPONSE, context.response().getOutputAsString())
        );
    }

    private void setPageResource(final String path) {
        final Page page = context.pageManager().getPage(path);
        context.request().setResource(page.getContentResource(CONTENT_RESOURCE_PATH));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this testing approach I have covered 87% of lines of code. Other 13% what is not covered is catching exceptions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WylQNwTs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.sanity.io/images/0a8atbln/production/a57dd21ea6b63fc5013a99c8e2eb56973a34e33c-1757x213.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WylQNwTs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.sanity.io/images/0a8atbln/production/a57dd21ea6b63fc5013a99c8e2eb56973a34e33c-1757x213.png" alt="" width="880" height="107"&gt;&lt;/a&gt; &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hKagcauQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.sanity.io/images/0a8atbln/production/078a9109aa65cb5ea3ef6ea72293e1fe1feb6ebb-1225x559.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hKagcauQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.sanity.io/images/0a8atbln/production/078a9109aa65cb5ea3ef6ea72293e1fe1feb6ebb-1225x559.png" alt="" width="880" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other good examples for testing in AEM would be components. For every component you have requirements. To achieve those requirements you will probaly create several classes like OSGi service, some Utils, Records and those requirements you will publicly exposed through Sling model to view layer. Ideal candidats for testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sum up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you don't write tests, start writing it&lt;/li&gt;
&lt;li&gt;Test requirements not implementation&lt;/li&gt;
&lt;li&gt;Developers should have time for writing tests&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>junit5</category>
      <category>mockito</category>
      <category>java</category>
    </item>
    <item>
      <title>Really? Preselected checkbox not working, common AEM?</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Mon, 01 Jun 2020 07:00:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/really-preselected-checkbox-not-working-common-aem-4n11</link>
      <guid>https://dev.to/mkovacek/really-preselected-checkbox-not-working-common-aem-4n11</guid>
      <description>&lt;h2&gt;
  
  
  Why one simple preselected checkbox doesn't work in page properties?
&lt;/h2&gt;

&lt;p&gt;Today I needed to add one, simple checbox in page properties and it should be preselected by default. Sounds simple, 5 min task, yea sure....&lt;/p&gt;

&lt;p&gt;Since I'm not doing this stuff so often I needed to check &lt;a href="https://helpx.adobe.com/experience-manager/6-5/sites/developing/using/reference-materials/granite-ui/api/jcr_root/libs/granite/ui/components/coral/foundation/form/checkbox/index.html"&gt;granite documentation&lt;/a&gt; to see how to achive it. Looks simple, I found properties what I need ( &lt;strong&gt;value&lt;/strong&gt; , &lt;strong&gt;uncheckedValue&lt;/strong&gt; , &lt;strong&gt;checked&lt;/strong&gt; ).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;showSomething
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
    cq:showOnCreate="{Boolean}true"
    text="Please be preselected by default"
    name="./showSomething"
    checked="{Boolean}true" // tried also with string "true"
    value="{Boolean}true" // "true"
    uncheckedValue="{Boolean}false" /&amp;gt; //"false"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And of course it doesn't work in page properties just in component dialog...&lt;/p&gt;

&lt;p&gt;Since I'm tired of doing some hacks or writing Javascript to get this stuff working, I just switched dialog to &lt;strong&gt;hideSomething&lt;/strong&gt; so that not preselected checkbox have sense.&lt;/p&gt;

&lt;p&gt;I don't understand how that kind of simple things doesn't work as expected OOTB.&lt;/p&gt;

&lt;p&gt;Share your ridiculous cases which doesn't work? :D&lt;/p&gt;

</description>
      <category>aem</category>
      <category>adobeexperiencemanager</category>
    </item>
    <item>
      <title>Updating AEM content with Sling Pipes</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Fri, 22 May 2020 13:00:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/updating-aem-content-with-sling-pipes-2d5i</link>
      <guid>https://dev.to/mkovacek/updating-aem-content-with-sling-pipes-2d5i</guid>
      <description>&lt;p&gt;You are still updating content manually? Try out Sling pipes.&lt;/p&gt;

&lt;p&gt;Several weeks ago I saw some tweet how Adobe guys are using Sling pipes for updating their AEM content, so I was curious and I try it out.&lt;/p&gt;

&lt;p&gt;Let's see what I have find out...&lt;/p&gt;

&lt;p&gt;Sling pipes is simple tool for executing CRUD operations over resources in AEM repository. But is it powerful enough to replace your groovy scripts? Let's discover it in following examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Just to get your attention, let's say in past you have made some bad decision during development of some of your component and now you need to update all content with that component. With Sling Pipes that is very easy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .echo("/content/yoursite")
  .$("nt:unstructured[sling:resourceType=projectName/components/content/imageSlider]")
  .write("autoplay", true)
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5 lines? Yes, you can set &lt;em&gt;autoplay property&lt;/em&gt; to true in all your content with &lt;em&gt;imageSlider&lt;/em&gt; component in 5 lines, and I'm pretty much sure you can't do that so simply with your groovy code, or you can do it manually in crx/de for whole day...&lt;/p&gt;

&lt;p&gt;So what the hell is happening in this code, what are this methods?!&lt;/p&gt;

&lt;p&gt;I will explain it later in more details, but in tl;dr;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;plumber&lt;/strong&gt; is &lt;strong&gt;OSGi&lt;/strong&gt; service and to start of all this magic you need to pass &lt;strong&gt;resourceResolver&lt;/strong&gt; object to &lt;strong&gt;newPipe&lt;/strong&gt; method. After that you need to call pipes (methods) to achieve what you want. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;echo("path")&lt;/strong&gt; is used to receive root resource on specific path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$(expression)&lt;/strong&gt; is wrapper of Sling query expressions and I'm using it here to filter out specific resources with imageSlider component&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;write(conf)&lt;/strong&gt; is pipe to write/update resources/properties, in this case I'm creating or updating autoplay property with value true&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;run()&lt;/strong&gt; is used to execute code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still interested? Let me explain you Sling pipes in more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Sling Pipes
&lt;/h2&gt;

&lt;p&gt;Sling pipes is a tool set for doing extract - transform - load operations by chaining proven code blocks. A &lt;strong&gt;sling pipe&lt;/strong&gt; is essentially a sling resource stream, encapsulating a well-known sling operations.&lt;/p&gt;

&lt;p&gt;There are 3 types of pipes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;reader pipes&lt;/strong&gt; - used to get resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;writer pipes&lt;/strong&gt; - for writing/updating resources in repository, depending on configuration and input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;logical pipes -&lt;/strong&gt; they refer to other pipes, chaining them or using their results in a general way&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reader pipes
&lt;/h3&gt;

&lt;p&gt;Used to get resources.&lt;/p&gt;

&lt;h4&gt;
  
  
  Base pipe: echo(path)
&lt;/h4&gt;

&lt;p&gt;Used to receive resource on given path&lt;/p&gt;

&lt;p&gt;Let's say you want to get "/content/we-retail" resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .echo("/content/we-retail")
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  XPathPipe: xpath(expr)
&lt;/h4&gt;

&lt;p&gt;Used to receive resource with given xpath query&lt;/p&gt;

&lt;p&gt;Let's say you want to get all &lt;em&gt;nt:unstructured&lt;/em&gt; resources with &lt;em&gt;heroimage&lt;/em&gt; component under "/content/we-retail".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .xpath("/jcr:root/content/we-retail//element(*, nt:unstructured)[(@sling:resourceType = 'weretail/components/content/heroimage')]")
  .run()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Sling Query Pipes
&lt;/h4&gt;

&lt;p&gt;Wrapper for executing Sling Query methods in sling pipe way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Find Pipe: $(expr)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Children Pipe: children(expr)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Siblings Pipe: siblings(expr)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parent Pipe: parent()&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Closest Pipe: closest(expr)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is more details about &lt;a href="https://sling.apache.org/documentation/bundles/sling-query.html"&gt;Sling Query&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same example as previous one, let's say you want to get all resources with &lt;em&gt;heroimage&lt;/em&gt; component under "/content/we-retail".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .echo("/content/we-retail")
  .$("nt:unstructured[sling:resourceType=weretail/components/content/heroimage]")
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writer pipes
&lt;/h3&gt;

&lt;p&gt;Used to modify resources.&lt;/p&gt;

&lt;h4&gt;
  
  
  Write Pipe: write(conf)
&lt;/h4&gt;

&lt;p&gt;Used to write given nodes and properties to current input resource&lt;/p&gt;

&lt;p&gt;Let's say you want to create/update property "foo" with value "bar" and property "foo2" with value "bar2".&lt;/p&gt;

&lt;p&gt;Please let me know is it possible to write different values except strings, like long, date, boolean?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .echo("/content/we-retail")
  .write('foo','bar','foo2','bar2')
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  PathPipe: mkdir(expr)
&lt;/h4&gt;

&lt;p&gt;Used to create resource on given location.&lt;/p&gt;

&lt;p&gt;Let's you want to create resource "master" on "/content/we-retail/master" location. This will create "sling:Folder" node.&lt;/p&gt;

&lt;p&gt;Please let me know if is it possible to specify different jcr:primaryType?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .mkdir("/content/we-retail/master")
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  MovePipe: mv(expr)
&lt;/h4&gt;

&lt;p&gt;Moves resource or property to target path&lt;/p&gt;

&lt;p&gt;Following example will move master resource from "/content/we-retail/master" to "/content/we-retail/language-master".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .echo("/content/we-retail/master")
  .mv("/content/we-retail/language-master")
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  RemovePipe: rm()
&lt;/h4&gt;

&lt;p&gt;Removes resource or property on specific location.&lt;/p&gt;

&lt;p&gt;Following example will remove master resource on given path "/content/we-retail/master".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .echo("/content/we-retail/master")
  .rm()
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  PackagePipe: pkg(expr)
&lt;/h4&gt;

&lt;p&gt;Creates a package and add current resource as a filter.&lt;/p&gt;

&lt;p&gt;Following example will package all we retail content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plumber.newPipe(resourceResolver)
  .echo("/content/we-retail")
  .pkg("/etc/packages/we-retail-content.zip")
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to use it in your AEM project
&lt;/h2&gt;

&lt;p&gt;Okey, how to run all this in your AEM project? You can configure and execute a pipes with java, groovy console, http, or jmx&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Dependencies&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;You need to add &lt;strong&gt;"org.apache.sling.query"&lt;/strong&gt; &amp;amp; &lt;strong&gt;"org.apache.sling.pipes"&lt;/strong&gt; as dependencies and you need to embedded them as part of your bundle or install it as separate bundles (my recommended way).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.sling&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;org.apache.sling.query&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;4.0.2&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.sling&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;org.apache.sling.pipes&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.1.0&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;

&amp;lt;plugin&amp;gt;
  &amp;lt;groupId&amp;gt;org.apache.jackrabbit&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;filevault-package-maven-plugin&amp;lt;/artifactId&amp;gt;
  &amp;lt;extensions&amp;gt;true&amp;lt;/extensions&amp;gt;
  &amp;lt;configuration&amp;gt;
      &amp;lt;allowIndexDefinitions&amp;gt;true&amp;lt;/allowIndexDefinitions&amp;gt;
      &amp;lt;packageType&amp;gt;mixed&amp;lt;/packageType&amp;gt;
      &amp;lt;group&amp;gt;pipes&amp;lt;/group&amp;gt;
      &amp;lt;embeddeds&amp;gt;
          &amp;lt;!--sling pipes related--&amp;gt;
          &amp;lt;embedded&amp;gt;
              &amp;lt;groupId&amp;gt;org.apache.sling&amp;lt;/groupId&amp;gt;
              &amp;lt;artifactId&amp;gt;org.apache.sling.query&amp;lt;/artifactId&amp;gt;
              &amp;lt;target&amp;gt;/apps/pipes-vendor-packages/application/install&amp;lt;/target&amp;gt;
          &amp;lt;/embedded&amp;gt;
          &amp;lt;embedded&amp;gt;
              &amp;lt;groupId&amp;gt;org.apache.sling&amp;lt;/groupId&amp;gt;
              &amp;lt;artifactId&amp;gt;org.apache.sling.pipes&amp;lt;/artifactId&amp;gt;
              &amp;lt;target&amp;gt;/apps/pipes-vendor-packages/application/install&amp;lt;/target&amp;gt;
          &amp;lt;/embedded&amp;gt;
      &amp;lt;/embeddeds&amp;gt;
  &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Code execution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You can write in &lt;strong&gt;groovy console&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def plumber = getService("org.apache.sling.pipes.Plumber")

plumber.newPipe(resourceResolver)
  .echo("/content/yoursite")
  .$("nt:unstructured[sling:resourceType=projectName/components/content/imageSlider]")
  .write("autoplay", true)
  .run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can write in you &lt;strong&gt;Java class&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component(service = SlingPipesExamples.class, immediate = true)
public class SlingPipesExamples {

    private static final Logger logger = LoggerFactory.getLogger(SlingPipesExamples.class);

    private static final String SERVICE_USER = "sling-pipes-service-user";

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private Plumber plumber;

    public void example() {
        try (final ResourceResolver resourceResolver = this.resourceResolverFactory.getServiceResourceResolver(this.getAuthenticationInfo())) {
            PipeBuilder pipeBuilder = this.plumber.newPipe(resourceResolver);
            // your logic with pipes
        } catch (final LoginException e) {
            logger.error("Exception during creating service resource resolver ", e);
        }
    }

    private Map&amp;lt;String, Object&amp;gt; getAuthenticationInfo() {
        return Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, SERVICE_USER);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can create resource on specific location e.g &lt;strong&gt;"/etc/sling-pipes/example"&lt;/strong&gt; and execute it with HTTP request&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
  jcr:description="Create directory pipe"
  jcr:primaryType="sling:OrderedFolder"
  sling:resourceType="slingPipes/mkdir"
  expr="/content/sling-pipes/foo"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sum up
&lt;/h3&gt;

&lt;p&gt;Basicly that's it what I discover so far. In general I like Sling Pipes, specialy how with few lines of code you can do a lot of stuff. Didn't try with some more complex examples but I guess it would be almost the same.&lt;/p&gt;

&lt;p&gt;For more details take a look into &lt;a href="https://sling.apache.org/documentation/bundles/sling-pipes.html"&gt;offical documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let me know if for &lt;strong&gt;mkdir&lt;/strong&gt; pipe is possible to specify different jcr:primaryType? If it's only "sling:Folder" then 👎&lt;/p&gt;

&lt;p&gt;Also for &lt;strong&gt;write&lt;/strong&gt; pipe, is it possible to write in property some value different then String?&lt;/p&gt;

&lt;p&gt;I hope that you learn something and that you will give a try Sling Pipes. Let me know what you think and what are your experiences with Sling Pipes :)&lt;/p&gt;

</description>
      <category>apachesling</category>
      <category>slingpipes</category>
      <category>aem</category>
      <category>java</category>
    </item>
    <item>
      <title>Getting started with Scala</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Mon, 17 Oct 2016 22:00:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/getting-started-with-scala-1oci</link>
      <guid>https://dev.to/mkovacek/getting-started-with-scala-1oci</guid>
      <description>&lt;p&gt;So you want to write your first Scala application? Before that, you need to install Scala on your PC and know some basic syntax.&lt;/p&gt;

&lt;p&gt;First, you need to install &lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html" rel="noopener noreferrer"&gt;&lt;em&gt;Java JDK&lt;/em&gt;&lt;/a&gt; and &lt;a href="http://www.scala-lang.org/download/" rel="noopener noreferrer"&gt;&lt;em&gt;Scala binaries&lt;/em&gt;&lt;/a&gt;. To check if you have successfully installed Scala just write command “scala” in command prompt or terminal and if everything is OK you will see a welcome message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2F0a8atbln%2Fproduction%2F4e0761724cc5042df19ae703687684c393436d30-819x161.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2F0a8atbln%2Fproduction%2F4e0761724cc5042df19ae703687684c393436d30-819x161.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scala has &lt;em&gt;REPL&lt;/em&gt; (Read-Eval-Print-Loop) which allows you to write Scala code and immediately see results of your code in command prompt/terminal. &lt;em&gt;REPL&lt;/em&gt; allows you to experiment with all kind of Scala expressions and immediately see results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2F0a8atbln%2Fproduction%2Fdbaaeacb001ee409f5ddff651278b9d763026fdf-818x430.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2F0a8atbln%2Fproduction%2Fdbaaeacb001ee409f5ddff651278b9d763026fdf-818x430.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don’t like cmd/terminal you can use your favorite IDE or text editor. IntelliJ, Eclipse and NetBeans have good support for writing Scala code. Also if you want REPL in your IDE you have to create Scala worksheet and in them you can experiment with your code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2F0a8atbln%2Fproduction%2Fb41878d2de3624ca31e987c445632da7f5a0a16c-639x136.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2F0a8atbln%2Fproduction%2Fb41878d2de3624ca31e987c445632da7f5a0a16c-639x136.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Scala syntax&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Variable declarations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Scala, you have two types of variables, immutable and mutable. Immutable variables are read-only and that means when you declare a variable, later you can’t assign new values/objects to that variable. An immutable variable is declared with keyword &lt;strong&gt;&lt;em&gt;val&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val name: String = "Scala"

//shorter
val newName = "Scala"

// you can't assign new value
newName = "Java" // Error: reassignment to val name = "Java"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mutable variables are declared with keyword &lt;strong&gt;&lt;em&gt;var&lt;/em&gt;&lt;/strong&gt; and later you can assign new values/objects to that variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var age = 23

//you can assign new values
age = 24
age = 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So keywords &lt;strong&gt;&lt;em&gt;val&lt;/em&gt;&lt;/strong&gt; (immutable) and &lt;strong&gt;&lt;em&gt;var&lt;/em&gt;&lt;/strong&gt; (mutable) specify whether the reference can be changed to a different object (var) or not (val).&lt;/p&gt;

&lt;p&gt;To declare variable you need to choose if you want &lt;strong&gt;&lt;em&gt;immutable&lt;/em&gt;&lt;/strong&gt; (val) or &lt;strong&gt;&lt;em&gt;mutable&lt;/em&gt;&lt;/strong&gt; (var) variable. Than you write name, after name you optionally write type of variable and on right side of equality you assign some value, object or expression. Defining type is optional because compiler can figure out it from right side of equality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val nameOfImmutableVariable: type = value
var nameOfMutableVariable: type = value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Method declarations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Method declarations start with keyword &lt;strong&gt;&lt;em&gt;def&lt;/em&gt;&lt;/strong&gt;   &lt;strong&gt;,&lt;/strong&gt; followed by optional argument list, optional return type and after equals sign body of the method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def methodName (argument: argumentType): returnType = {
  //body of the method
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Methods that don’t return some value for return type have a &lt;strong&gt;&lt;em&gt;Unit&lt;/em&gt;&lt;/strong&gt; type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def printHelloWorld: Unit = {
  println("Hello world")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, you don’t have to write return keyword to return some value from the method, it’s enough to write value or some object that you want to return.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def sum(a: Int, b: Int): Int = {
  a + b
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scala methods can be written even more a concisely. Return type doesn’t have to be specified and parentheses around the body of the short method aren’t required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def sum(a: Int, b: Int) = a + b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scala is flexible when you working with methods. You can call object methods in two ways, the first way is to call a method on the object with dot character and the second way is like you write a sentence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;someobject.methodName()
//or
someobject methodName()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also it’s important to mention when you call method which don’t take argument and has no side effects (pure functions) like mutating the state of objects or I/O operations than when you call method you don’t have to put rounded brackets after the method name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List(1,2,3,4,5).size // 5
List(1,2,3,4,5).size() // error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Classes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To create Scala class you need to write keyword “class” and name of the class. A primary constructor of Scala class makes a combination of constructor parameters which are defined after class name and expressions and methods that are called in the body of the class. Depends on constructor parameters and fields types Scala automatically generates getter and setter methods. So if a field is mutable than are generated both getter and setter methods and if a field is an immutable than is generated the only getter method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Person(val firstName: String, val lastName: String){
  println("beginning of the constructor")
  var age = 25
  def printInfo = println("Firstname: " + firstName + ", lastname: " + lastName + ", age: " + age)
  printInfo
  println("end of the constructor")
}

val person = new Person("Matija","Kovacek")
/*
beginning of the constructor
Firstname: Matija, lastname: Kovacek, age: 25
end of the constructor
*/
// getter 
person.firstName
//setter
person.age = 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No semicolons&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You may have noticed that in previous examples I didn’t use semicolons. Scala is treating end of the line as the end of the statement or an expression so you don’t have to put semicolons. You only have to use semicolons when you want to put multiple statements or expressions on the same line.&lt;/p&gt;

</description>
      <category>scala</category>
    </item>
    <item>
      <title>I started learning Scala programming language and I don’t regret</title>
      <dc:creator>Matija Kovaček</dc:creator>
      <pubDate>Sun, 18 Sep 2016 08:30:00 +0000</pubDate>
      <link>https://dev.to/mkovacek/i-started-learning-scala-programming-language-and-i-don-t-regret-5h96</link>
      <guid>https://dev.to/mkovacek/i-started-learning-scala-programming-language-and-i-don-t-regret-5h96</guid>
      <description>&lt;p&gt;Couple months ago I start learning Scala programming language and I want to share what I have learned so far.&lt;/p&gt;

&lt;p&gt;I will try in several weeks maybe months share everything I know about Scala.&lt;/p&gt;

&lt;h2&gt;
  
  
  So let (scala) games begin.
&lt;/h2&gt;

&lt;p&gt;Scala is a programming language created by professor Martin Odersky. It’s object-oriented and also a functional programming language. So you can write your code using OOP, FP or both. In Scala everything is an object, primitive types don’t exist and operators are methods. Also, interesting OOP stuff are trait interfaces, case and object classes. Functions are first-class citizens in FP, so you can assign them to variables and pass to other functions like other objects. Other interesting FP concepts in Scala are immutable variables, immutable data structures, and pattern matching.&lt;/p&gt;

&lt;p&gt;Scala is statically typed language but it feels like it’s dynamically typed. I call Scala like a modern version of Java with some mix of Ruby and Python. Scala minimizes unnecessary syntax (such as declarations of variable type) and with the flexible syntax, you have feeling that you write code in the dynamically typed language.&lt;/p&gt;

&lt;p&gt;It’s a JVM language, but there is a .NET version of Scala, also exists Scala.js and Scala-native is in a developing mode.&lt;/p&gt;

&lt;p&gt;Who uses Scala? Twitter, LinkedIn, Netflix, Tumbler, Foursquare …&lt;/p&gt;

&lt;h2&gt;
  
  
  And for the end, I want to share few interesting quotes.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why Twitter choose Scala:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;„ … The other big reason we looked at Scala was that, although we’ve run into problems with Ruby, we like &lt;strong&gt;the flexibility of the language&lt;/strong&gt;. We like that it’s such &lt;strong&gt;a full featured language&lt;/strong&gt; , that &lt;strong&gt;it’s fun to code in&lt;/strong&gt;. It’s the same reason so many Java people end up writing Ruby after they leave some big enterprise company. &lt;strong&gt;They want to have fun day to day&lt;/strong&gt;. We didn’t want to leave that behind and go to a language with a very dry, businesslike community, like C++, for example. We know that people write super high-performance code in C++, and engineers like Steve and Robey have had experience with that. But we wanted to be using a language that we’re really passionate about, and it seemed worth taking a gamble on Scala.“ — Alex Payne&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What creators of Java, Groovy, and jRuby thinks about Scala:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“If I were to pick a language to use today other than Java, it would be Scala.” — James Gosling, creator of Java&lt;/p&gt;

&lt;p&gt;“Scala, it must be stated, is the current heir apparent to the Java throne. No other language on the JVM seems as capable of being a “replacement for Java” as Scala, and the momentum behind Scala is now unquestionable. While Scala is not a dynamic language, it has many of the characteristics of popular dynamic languages, through it’s rich and flexible type system, it’s sparse and clean syntax, and its marriage of functional and object paradigms.” — Charles Nutter, creator of JRuby&lt;/p&gt;

&lt;p&gt;“I can honestly say if someone had shown me the Programming in Scala book by Martin Odersky, Lex Spoon &amp;amp; Bill Venners back in 2003 I’d probably have never created Groovy.” — James Strachan, creator of Groovy.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://devz.life/blog/getting-started-with-scala/"&gt;how to get started with Scala&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>scala</category>
    </item>
  </channel>
</rss>
