java Articles / Blogs / Perficient https://blogs.perficient.com/tag/java/ Expert Digital Insights Mon, 27 Oct 2025 08:02:27 +0000 en-US hourly 1 https://blogs.perficient.com/files/favicon-194x194-1-150x150.png java Articles / Blogs / Perficient https://blogs.perficient.com/tag/java/ 32 32 30508587 Spring Boot + OpenAI : A Developer’s Guide to Generative AI Integration https://blogs.perficient.com/2025/10/27/spring-boot-openai-a-developers-guide-to-generative-ai-integration/ https://blogs.perficient.com/2025/10/27/spring-boot-openai-a-developers-guide-to-generative-ai-integration/#respond Mon, 27 Oct 2025 08:02:27 +0000 https://blogs.perficient.com/?p=387157

Introduction

In this blog, we’ll explore how to connect OpenAI’s API with a Spring Boot application, step by step.

We’ll cover the setup process, walk through the implementation with a practical example.

By integrating OpenAI with Spring Boot, you can create solutions that are not only powerful but also scalable and reliable.

Prerequisites

  • Java 17+
  • Maven
  • Spring Boot (3.x recommended)
  • OpenAI API Key (get it from platform.openai.com)
  • Basic knowledge of REST APIs

OpenAI’s platform helps developers to understand how to prompt a models to generate meaningful text. It’s basically a cheat sheet for how to communicate to AI so it gives you smart and useful answers by providing prompts. 

Implementation in Spring Boot

To integrate OpenAI’s GPT-4o-mini model into a Spring Boot application, we analyzed the structure of a typical curl request and response provided by OpenAI.

API docs reference:

https://platform.openai.com/docs/overview

https://docs.spring.io/spring-boot/index.html

Curl Request

<html>
curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [
      {"role": "assistant", "content": "Hello"},
      {"role": "user", "content": "Hi"}
    ]
  }'
</html>

Note-

“role”: “user” – Represents the end-user interacting with the assistant

“role”: “assistant” – Represents the assistant’s response.

The response generated from the model and it looks like this:

{
  "id": "chatcmpl-B9MBs8CjcvOU2jLn4n570S5qMJKcT",
  "object": "chat.completion",
  "created": 1741569952,
  "model": "gpt-4o-mini-2025-04-14",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I assist you today?",
        "refusal": null,
        "annotations": []
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 19,
    "completion_tokens": 10,
    "total_tokens": 29,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  },
  "service_tier": "default"
}

 

Controller Class:

In below snippet, we will explore a simple spring boot controller to interact with Open AI’s API. When end user sends a prompt to that url (e.g /bot/chat?prompt=what is spring boot), the controller reads the model name and API url from applocation.properties file. It then creates a request using prompt provided and sends it to Open AI using rest call(RestTemplate). After verifying the request, OpenAI sends back a response.

@RestController
@RequestMapping("/bot")
public class GenAiController {

    @Value("${openai.model}")
    private String model;

    @Value(("${openai.api.url}"))
    private String apiURL;

    @Autowired
    private RestTemplate template;

    @GetMapping("/chat")
    public String chat(@RequestParam("prompt") String prompt) {
        GenAiRequest request = new GenAiRequest(model, prompt);
        System.out.println("Request: " + request );
        GenAIResponse genAIResponse = template.postForObject(apiURL, request, GenAIResponse.class);
        return genAIResponse.getChoices().get(0).getMessage().getContent();
    }

 

Configuration Class:

Annotated with @Configuration, this class defines beans and settings for the application context. Pulling the Open API key from properties file and the a customized RestTemplate is created and configured to include the Authorization Bearer <API_KEY> header in all requests. This setup ensures that every call to OpenAI’s API is authenticated without manually adding headers in each request.

@Configuration
public class OpenAIAPIConfiguration {

    @Value("${openai.api.key}")
     private String openaiApiKey;

    @Bean
    public RestTemplate template(){
        RestTemplate restTemplate=new RestTemplate();
        restTemplate.getInterceptors().add((request, body, execution) -> {
            request.getHeaders().add("Authorization", "Bearer " + openaiApiKey);
            return execution.execute(request, body);
        });
        return restTemplate;
    }
    
}

Require getters and setters for request and response classes:

Based on the Curl structure and response, we generated the corresponding request and response java classes with appropriate getters and setters with selected attributes to repsesent request and response object. These getter/setter classes help turn JSON data into objects we can use in code, and also turn our code’s data back into JSON when interacting to the OpenAI API. We implemented a bot using the gpt-4o-mini model, integrating it with a REST controller and also handled the authentication via the API key.

//Request
@Data
public class GenAiRequest {

    private String model;
    private List<GenAIMessage> messages;

    public List<GenAIMessage> getMessages() {
        return messages;
    }

    public GenAiRequest(String model, String prompt) {
        this.model = model;
        this.messages = new ArrayList<>();
        this.messages.add(new GenAIMessage("user",prompt));
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GenAIMessage {

    private String role;
    private String content;   
    
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}

//Response
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GenAIResponse {

    private List<Choice> choices;

    public List<Choice> getChoices() {
        return choices;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Choice {

        private int index;
        private GenAIMessage message;
        public GenAIMessage getMessage() {
            return message;
        }
        public void setMessage(GenAIMessage message) {
            this.message = message;
        }

    }

}

 

Essential Configuration for OpenAI Integration in Spring Boot

To connect your Spring Boot application with OpenAI’s API, you need to define a few key properties in your application.properties or application.yml file:

  • server.port: Specifies the port on which your Spring Boot application will run. You can set it to any available port like 8080, 9090, etc. (The default port for a Spring Boot application is 8080)
  • openai.model: Defines the OpenAI model to be used. In this case, gpt-4o-mini is selected for lightweight and efficient responses.
  • openai.api.key: Your secret API key from OpenAI. This is used to authenticate requests. Make sure to keep it secure and never expose it publicly.
  • openai.api.url: The endpoint URL for OpenAI’s chat completion API. (This is where your application sends prompts and receives responses)
server.port=<add server port>
openai.model=gpt-4o-mini
openai.api.key=	XXXXXXXXXXXXXXXXXXXXXXXXXXXX
openai.api.url=https://api.openai.com/v1/chat/completions

 

Postman Collection:

GET API: http://localhost:<port>/bot/chat?prompt=What is spring boot used for ?

Content-Type: application/json

Prompt

Usage of Spring Boot + OpenAI Integration

  • AI-Powered Chatbots: Build intelligent assistants for customer support, internal helpdesks, or onboarding systems.
  • Content Generation Tools: Automate blog writing, email drafting, product descriptions, or documentation, generate personalized content based on user input.
  • Code Assistance & Review: Create tools that help developers write, refactor, or review code using AI, Integrate with IDEs or CI/CD pipelines for smart suggestions.
  • Data Analysis & Insights: Use AI to interpret data, generate summaries, answer questions about datasets combine with Spring Boot APIs to serve insights to dashboards or reports.
  • Search Enhancement: Implement semantic search or question-answering systems over documents or databases, use embeddings and GPT to improve relevance and accuracy.
  • Learning & Training Platforms: Provide personalized tutoring, quizzes, and explanations using AI & adapt content based on user performance and feedback.
  • Email & Communication Automation: Draft, summarize, or translate emails and messages, integrate with enterprise communication tools.
  • Custom usages: In a business-to-business context, usage can be customized according to specific client requirements.
]]>
https://blogs.perficient.com/2025/10/27/spring-boot-openai-a-developers-guide-to-generative-ai-integration/feed/ 0 387157
Why Learn Quarkus : Supersonic, Subatomic JAVA ? https://blogs.perficient.com/2025/01/23/why-learn-quarkus-supersonic-subatomic-java/ https://blogs.perficient.com/2025/01/23/why-learn-quarkus-supersonic-subatomic-java/#comments Thu, 23 Jan 2025 13:13:14 +0000 https://blogs.perficient.com/?p=376136

If you’re a developer working in today’ tech world, you’ve probably heard the buzz about Quarkus. But what is it, and why should you care? Let’s break it down in simple terms.

What is Quarkus?

Quarkus is said to be “Supersonic Subatomic Java”. Catchy sentence, isn’t it ?  I quite like it because supersonic gives you the impression of speed. Subatomic gives you the sensation of size, atomic, so you can think that everything is small and yes, indeed it is. Quarkus is a modern framework designed for Java developers who want to build applications that are lightning-fast and ready for the cloud. Think of it as a new-and-improved toolbox that helps you create better, faster apps with less hassle.

Traditionally, Java applications have been powerful but also resource-heavy. They take longer to start, use more memory, and aren’t always the best fit for modern platforms like Kubernetes or serverless environments. Quarkus flips this script by being lightweight, fast, and cloud-native.

Why Should You Learn Quarkus?

Quarkus have become essential to streamline microservice development. Here are a few reasons to add Quarkus to your developer skillset:

1. Fast Start-Up Times

Imagine you’re working on an app that needs to start quickly—whether it’s for scaling up during peak hours or running short-lived serverless functions. Traditional Java apps can take several seconds (or even minutes) to boot up. With Quarkus, start-up times are measured in milliseconds. Faster start-ups mean happier users and more efficient systems.

2. Low Memory Usage

In the world of cloud computing, you’re often paying for resources like CPU and memory. Quarkus uses far less memory compared to traditional Java frameworks. This makes it ideal for running multiple applications on the same infrastructure without breaking the bank.

3. Built for Cloud and Kubernetes

Today, apps are increasingly run in cloud environments like Kubernetes. Quarkus is designed with these platforms in mind. It simplifies packaging, deployment, and scaling, making it easy to build apps that work seamlessly in modern cloud ecosystems.

4. Developer Joy

Let’s be honest: writing code can sometimes feel tedious. Quarkus aims to bring joy back to the process with features like hot reload. This means you can see your changes instantly, without restarting your app. It’s like having a magic wand that speeds up your development and testing.

5. Compatible with Your Existing Skills

If you’re already a Java developer, you don’t need to start from scratch. Quarkus supports familiar frameworks like Spring, Hibernate, and RESTEasy. You can bring your existing knowledge and get started quickly, making the learning curve much smoother.

6. Future-Proof Your Career

The tech industry is always evolving. By learning Quarkus, you’re positioning yourself as a developer who’s ready for the future. Companies are increasingly adopting Quarkus to build scalable, cloud-native applications. Mastering it can make you a sought-after professional.

How Do You Start?

Getting started with Quarkus is easy. Here’s a simple plan:

  1. Visit the Quarkus Website: Head over to quarkus.io to explore the official documentation and guides.
  2. Try the Quickstart: Follow the quickstart projects to see how Quarkus works.
  3. Build Something Small: Create a simple REST API or a microservice to get hands-on experience.
  4. Leverage the Community: Join forums and communities to learn from others and share your experiences.

Conclusion

Quarkus is more than just a buzzword; it’s a game-changer for Java developers. With its blazing-fast performance, cloud-native features, and developer-friendly tools, it’s a must-learn technology for anyone looking to stay ahead in the world of software development.
So why wait? Dive into Quarkus today and take your skills to the next level. To help you out more I will bring more practical guides through my blogs! Stay tuned.

]]>
https://blogs.perficient.com/2025/01/23/why-learn-quarkus-supersonic-subatomic-java/feed/ 1 376136
The risk of using String objects in Java https://blogs.perficient.com/2024/10/25/the-risk-of-using-string-objects-in-java/ https://blogs.perficient.com/2024/10/25/the-risk-of-using-string-objects-in-java/#respond Fri, 25 Oct 2024 23:23:46 +0000 https://blogs.perficient.com/?p=370962

If you are a Java programmer, you may have been incurring an insecure practice without knowing. We all know (or should know) that is not safe to store unencrypted passwords in the database because that might compromise the protection of data at rest. But that is not the only issue, if at any time in our code there is an unencrypted password or sensitive data stored in a String variable even if it is temporary, then there could be a risk.

Why is there a risk?

String objects were not created to store passwords, they were designed to optimize space in our program. String objects in Java are “immutable” which means that after you create a String object and assign it some value, afterward you cannot remove the value nor modify it. I know you might be thinking that this is not true because you can assign “Hello World” to a given String object and in the following line assign it with “Goodbye, cruel world”, and that is technically correct. The problem is that the “Hello World” that you created first is going to keep living in the String pool even if you cannot see it.

What is the String pool?

Java uses a special memory area called the String pool to store String literals. When you create a String literal, Java checks the String pool first to see if an identical String already exists. If it does, Java will reuse the reference to the existing String, saving memory. This means that if you create 25.000 String objects and all of them have the value of “Michael Jackson” only one String Literal will be stored in memory and all variables will be pointing to the same one, optimizing the space in memory.

Ok, the object is in the String pool, where is the risk?

The String Object will remain in memory for some time before being deleted by the garbage collector. If an attacker has access to the content of the memory, they could obtain the password stored there.

Let’s see a basic example of this. The following code is creating a String object and assigning it with a secret password: “¿This is a secret password”. Then, that same object is overwritten 3 times, and the Instances Inspector of the Debugger will help us in locating String objects starting with the character “¿”.

Example 1 Code:

Code1

Example 1 Debugger:

Example1

 

As you can notice in the image when the debugger has gotten to the line 8, even after having changed three times the value of the String variable “a” and setting it to null at the end, all previous values remain in the memory, included our: “¿This is a secret password”.

 

Got it. Just avoiding creating String variables will solve the problem, right?

It is not that simple. Let us consider a second example. Now we are smarter, and we are going to use a char array to store the password instead of the String to avoid the issue of having it saved in the String pool. In addition, rather than having the secret password as literal in the code, it will be available unencrypted in a text file, which by the way is not recommended to save it unencrypted, but we will do it for this example. A BufferedReader is going to support reading the contents of the file.

Unfortunately, as you will see, password also exist in the String pool.

Example 2 Code:

Code2

Example 2 Debugger:

 

Example2 Debugger

This case is even more puzzling because in the code a String Object was never created, at least explicitly. The problem is that the BufferedReader.readLine() is returning a String Object temporarily and the content with the unencrypted password will remain in the String pool.

What can I do to solve this problem?

In this last example we will have the unencrypted password stored in a text file, we will use a BufferedReader to read the contents of the file, but instead of using the method BufferedReader.readLine()  that returns a String we are using the method BufferedReader.read() that stores the content of the file in a char array.  As seen in the debugger’s screenshot, this time the file’s contents are not available in the String pool.

Example 3 Code:

Code3

Example 3 Debugger:

Example3 Debugger

In summary

To solve this problem, consider following the principles listed below:

  1. Do not create String literals with confidential information in your code.
  2. Do not store confidential information in String objects. You can use other types of Objects to store this information such as the classic char array. After processing the data make sure to overwrite the char array with zeros or some random chars, just to confuse attackers.
  3. Avoid calling methods that will return the confidential information as String, even if you will not save that into a variable.
  4. Consider applying an additional security layer by encrypting confidential information. The SealedObject in Java is a great alternative to achieve this. The SealedObject is a Java Object where you can store sensitive data, you provide a secret key, and the Object is encrypted and serialized. This is useful if you want to transmit it and ensure the content remains unexposed. Afterward, you can decrypt it using the same secret key. Just one piece of advice, after decrypting it, please do not store it on a String object.
]]>
https://blogs.perficient.com/2024/10/25/the-risk-of-using-string-objects-in-java/feed/ 0 370962
AI Toolkits Magic: Automating CAPTCHA Recognition Using OpenCV and Tesseract https://blogs.perficient.com/2024/07/08/ai-toolkits-magic-automating-captcha-recognition-using-opencv-and-tesseract/ https://blogs.perficient.com/2024/07/08/ai-toolkits-magic-automating-captcha-recognition-using-opencv-and-tesseract/#comments Mon, 08 Jul 2024 05:31:18 +0000 https://blogs.perficient.com/?p=365295

OpenCV and Tesseract can be associated with Artificial Intelligence due to their involvement in tasks that often fall under the AI umbrella, such as computer vision and text recognition. To automate solving image CAPTCHAs using Java, you will typically need several dependencies for tasks such as image processing, machine learning, and possibly computer vision.

OpenCV: A powerful library for computer vision and image processing. You can use the Java bindings for OpenCV.

Tesseract OCR: Tesseract OCR is an optical character recognition library that extracts text from images.

OpenCV (Open-Source Computer Vision Library)

  • Category: AI Toolkit for Computer Vision and Image Processing.
  • Purpose: Provides comprehensive tools for image and video processing, essential for many AI applications.
  • Capabilities: Includes image transformation, filtering, feature detection, object detection, and support for machine learning and deep learning.
  • Usage: Commonly used in AI projects for tasks like object detection, face recognition, and image classification.

Tesseract

  • Category: AI Toolkit for Optical Character Recognition (OCR)
  • Purpose: Converts images of text into machine-readable text using machine learning techniques.
  • Capabilities: Recognizes and extracts text from images, supporting multiple languages and fonts.
  • Usage: Utilized in AI projects for tasks such as document digitization, data extraction from scanned documents, and integrating text recognition into applications.

Step 1: Set up Dependencies

First, add the necessary dependencies to your pom.xml file:

Opencv dependencies

Tesseract dependencies

Step 2: Write the Java Code

Create a Java class to preprocess the CAPTCHA image and extract the text using Tesseract.

Captchasolver Code for image captcha

Step 3: Set up Tesseract Data

You must create your Tesseract data files and place them in a project directory (e.g., tessdata). but you do need at least the data file for the language used in the CAPTCHA. Tesseract uses trained data files (often referred to as “language data” or “Tessdata”) to recognize text in different languages.

For example, you only need the English-trained data file if your CAPTCHA contains English text. If contains text in another language, you’ll need the corresponding trained data file.

Step 4: Run the code

Ensure you have an image CAPTCHA in your project directory(e.g.,captcha//captcha.png) and adjust the paths in the code accordingly.

Captcha image under folder

Then, run the CaptchaSolver class.

Final Console Output of Extracted Image Text

Output of code

Explanation

  1. Image Preprocessing:
    • Load the image in grayscale mode.
    • Apply a binary threshold to convert the image to black and white.
    • Save the preprocessed image to disk.
  2. Text Extraction with Tesseract:
    • Initialize Tesseract and point it to the tessdata directory.
    • Process the preprocessed image with Tesseract to extract the text.

By running this code, you should be able to automate the solving of simple image CAPTCHAs. Adjust the preprocessing steps as necessary for more complex CAPTCHAs.

Summary

Referring to OpenCV and Tesseract as components of an “AI toolkit” accurately reflects their roles in enabling and enhancing AI applications, particularly in the domains of computer vision and text recognition. They are essential tools for implementing AI-driven solutions, making them integral parts of the AI development ecosystem.

]]>
https://blogs.perficient.com/2024/07/08/ai-toolkits-magic-automating-captcha-recognition-using-opencv-and-tesseract/feed/ 1 365295
Make Your Flutter Apps Soar with Pigeon Platform Channels https://blogs.perficient.com/2024/03/21/make-your-flutter-apps-soar-with-pigeon-platform-channels/ https://blogs.perficient.com/2024/03/21/make-your-flutter-apps-soar-with-pigeon-platform-channels/#respond Thu, 21 Mar 2024 18:08:29 +0000 https://blogs.perficient.com/?p=352054

Flutter is great framework for cross platform development. It allows you to make pixel perfect apps that are generated into native code, but what happens if you need to use existing code in iOS or Android directly? For situations like these, Flutter allows you to use platform channels.

Platform channels give you access to platform-specific APIs in a language that works directly with those APIs. Platform channels are available for Kotlin or Java on Android, Swift or Objective-C on iOS and macOS, C++ on Windows and C on Linux.

More information can be found on this here https://docs.flutter.dev/platform-integration/platform-channels

The platform APIs provided by Flutter work as intended, but the whole process is a bit cumbersome to set up. Pigeon allows us to use type safety and code generation to make this process a whole lot simpler.

Create a Pigeon Plugin

We will go ahead and create a simple example api.

Let’s start by creating a new plugin called pigeon_example

flutter create --org com.example --template=plugin --platforms=android,ios,linux,macos,windows -i swift pigeon_example
flutter pub add pigeon
flutter pub get

Platform Channel Types in Swift

Below is a list of supported types in Dart and their swift equivalents. We will use some of the most common types in our example

Dart TypesSwift Types
nullnil
boolNSNumber(value: Bool)
intNSNumber(value: Int32)
int, if 32 bits not enoughNSNumber(value: Int)
doubleNSNumber(value: Double)
StringString
Uint8ListFlutterStandardTypedData(bytes: Data)
Int32ListFlutterStandardTypedData(int32: Data)
Int64ListFlutterStandardTypedData(int64: Data)
Float32ListFlutterStandardTypedData(float32: Data)
Float64ListFlutterStandardTypedData(float64: Data)
ListArray
MapDictionary

Define Our API

In order to let Pigeon know what methods we’re going to be exposing we define our API in an abstract Dart class with the @HostApi() decorator, and its methods

Let’s define our Pigeon Example API in a new directory named pigeons.

import 'package:pigeon/pigeon.dart';

@HostApi()

abstract class ExampleApi {
bool getBool();
String getString();
func toggleValue();
}

Generate Pigeon Platform Code

Now we can let the Pigeon package do it’s magic and we can generate some code

dart run pigeon \
--input pigeons/example_api.dart \
--dart_out lib/example_api.dart \
--experimental_swift_out ios/Classes/ExampleApi.swift \
--kotlin_out ./android/app/src/main/kotlin/com/example/ExampleApi.kt \
--java_package "io.flutter.plugins"

Be sure that the paths to all of the files are correct or the next steps won’t work. Generate the code with the output for the platforms needed. This is example is going to focus on using Swift.

Add Method Implementation to the Runner

Next we need to write our native implementation of our methods. When doing this we need to add our files to the runner in Xcode to ensure that they run properly.

class ExampleApiImpl : ExampleApi{
var value = true;

func getBool(){
return value;
}
func toggleValue(){
    value = !value
  }
func getString(){
return "THIS IS AN EXAMPLE";
}

}

Add Pigeon Platform Channel to AppDelegate

You will also need to add this code in your AppDelegate.swift file

@UIApplicationMain

@objc class AppDelegate: FlutterAppDelegate {

override func application(

_ application: UIApplication,

didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?

) -> Bool {

GeneratedPluginRegistrant.register(with: self)

let exampleApi = ExampleApiImpl()

let controller : FlutterViewController = window?.rootViewController as! FlutterViewController

ExampleApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: exampleApi)




return super.application(application, didFinishLaunchingWithOptions: launchOptions)

}

}

 

Now you should be able to use your API in Dart code.

 

import 'package:flutter/material.dart';
import 'package:pigeon_example/example_api.dart';
import 'dart:async';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final exampleApi = ExampleApi();
  bool value = false;
  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    // Platform messages may fail, so we use a try/catch PlatformException.
    // We also handle the message potentially returning null.
    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: const Text('Plugin example app'),
            ),
            body:
                Column(mainAxisAlignment: MainAxisAlignment.center, children: [
              DefaultTextStyle(
                  style: Theme.of(context).textTheme.displayMedium!,
                  textAlign: TextAlign.center,
                  child: FutureBuilder<String>(
                    future: exampleApi
                        .getString(), // a previously-obtained Future<String> or null
                    builder:
                        (BuildContext context, AsyncSnapshot<String> snapshot) {
                      List<Widget> children = [];
                      if (snapshot.data!.isNotEmpty) {
                        children = <Widget>[
                          Text(snapshot.data ?? ''),
                        ];
                      }
                      return Center(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: children,
                        ),
                      );
                    },
                  )),
              Center(
                child: ElevatedButton(
                  child: const Text('Toggle Value'),
                  onPressed: () async {
                    await exampleApi.toggleValue();
                    var val = await exampleApi.getBool();
                    setState(() {
                      value = val;
                    });
                  },
                ),
              ),
              DefaultTextStyle(
                  style: Theme.of(context).textTheme.displayMedium!,
                  textAlign: TextAlign.center,
                  child: FutureBuilder<bool>(
                    future: exampleApi
                        .getBool(), // a previously-obtained Future<String> or null
                    builder:
                        (BuildContext context, AsyncSnapshot<bool> snapshot) {
                      List<Widget> children;
                      if (snapshot.data == true) {
                        children = <Widget>[
                          const Icon(
                            Icons.check_circle_outline,
                            color: Colors.green,
                            size: 60,
                          ),
                          Padding(
                            padding: const EdgeInsets.only(top: 16),
                            child: Text('Result: ${snapshot.data}'),
                          ),
                        ];
                      } else if (snapshot.data == false) {
                        children = <Widget>[
                          const Icon(
                            Icons.error_outline,
                            color: Colors.red,
                            size: 60,
                          ),
                          Padding(
                            padding: const EdgeInsets.only(top: 16),
                            child: Text('Result: ${snapshot.data}'),
                          ),
                        ];
                      } else {
                        children = const <Widget>[
                          SizedBox(
                            width: 60,
                            height: 60,
                            child: CircularProgressIndicator(),
                          ),
                          Padding(
                            padding: EdgeInsets.only(top: 16),
                            child: Text('Awaiting result...'),
                          ),
                        ];
                      }
                      return Center(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: children,
                        ),
                      );
                    },
                  ))
            ])));
  }
}

 

Now we can see the values from out example API in our Flutter UI. Button toggles will change our boolean value.

Simulator Screenshot Iphone 12 2024 03 21 At 12.56.11 Simulator Screenshot Iphone 12 2024 03 21 At 12.56.08

This same pattern can be used for any type of data supported by Pigeon.

Pigeon simplifies the process of creating platform channels. It also speeds up the process when multiple channels are needed. This becomes very valuable when you need a package that doesn’t have an implementation in Flutter. It’s a bit tricky to set up the first time, but once your scripts are written, modifying existing channels and creating new ones is a breeze.

For more information about Perficient’s Mobile Solutions expertise, subscribe to our blog or contact our Mobile Solutions team today!

 

]]>
https://blogs.perficient.com/2024/03/21/make-your-flutter-apps-soar-with-pigeon-platform-channels/feed/ 0 352054
Unleashing the Power of Custom Selenium Methods: A Guide to Stream Mapping https://blogs.perficient.com/2024/02/08/unleashing-the-power-of-custom-selenium-methods-a-guide-to-stream-mapping/ https://blogs.perficient.com/2024/02/08/unleashing-the-power-of-custom-selenium-methods-a-guide-to-stream-mapping/#respond Thu, 08 Feb 2024 06:08:25 +0000 https://blogs.perficient.com/?p=355979

In the ever-evolving landscape of web automation, Selenium has emerged as a powerful tool for testers and developers alike. One key aspect of Selenium’s flexibility lies in its ability to accommodate custom methods, offering a clear approach to handling complex scenarios. In this blog, we will delve into the realm of developing custom Selenium methods using the dynamic and expressive capabilities of stream mapping.

Building a Foundation

Before we dive into the complexities of stream mapping in Selenium, let’s establish a foundational understanding of the principles involved. Familiarity with Java and Selenium basics is recommended to fully grasp the concepts discussed in this guide.

Catch up on our previous blogs:

https://blogs.perficient.com/2024/01/03/unlocking-the-power-of-java-streams-simplifying-code-with-efficiency/

https://blogs.perficient.com/2024/02/07/streamlining-web-table-manipulation-with-selenium-and-java-streams/

Understanding Stream Mapping

Stream mapping, an integral feature of Java, provides a concise and expressive way to manipulate data collections. Leveraging this functionality in Selenium allows developers to create custom methods seamlessly interacting with web elements, providing a streamlined and efficient automation process.

We can see an example of custom build selenium using a stream mapper in the example mentioned below:

 

List<String>price= elementsList.stream().filter(s>s.getText().contains("Beans")). map(s>getPriceVeggi(s)).collect(Collectors.toList());
price.forEach(s->System.out.println(s));


}

private static String getPriceVeggie(WebElement s) {
String pricevalue= s.findElement(By.xpath("following-sibling::td[1]")).getText();
return pricevalue;
    

Explanation

This code snippet exemplifies our effective utilization of methods. We begin by converting elements into a stream, followed by filtering values based on the ‘contains’ specification. This process allows us to send only the values of specific elements rather than the entire stream, subsequently mapping the price of the contained values.

In scenarios requiring certain conditions to be met, we apply the filter method as usual to refine the values. Consequently, this approach precisely isolates the desired elements. The ‘getPriceVeggi’ method, situated at the bottom, is dedicated to crafting a logic that retrieves the value of a specific bean.

Input

package practice;

import java.util.List;
import java.util.stream.Collectors;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;

import io.github.bonigarcia.wdm.WebDriverManager;

public class WebTableSortingUsingStreams {

public static void main(String[] args) {
// TODO Auto-generated method stub

//Managing the browser
WebDriverManager.chromedriver().setup();
WebDriver driver = new ChromeDriver();

//Maximizing the browser
driver.manage().window().maximize();

//Navigating to the URL
driver.get("https://rahulshettyacademy.com/seleniumPractise/#/offers");

//Click on Column using the xpath
driver.findElement(By.xpath("//tr/th[1]")).click();

//capture all web elements of the column into list again by using xpath.
List<WebElement> elementsList = driver.findElements(By.xpath("//tr/td[1]"));

//capture text of all webelements into new(original) list, in this we are converting the elements of
//list into a stream format by making the use of .stream method and then mapping them using .map
List<String> originalList = elementsList.stream().map(s->s.getText()).collect(Collectors.toList());

//sorting method is used on the original list of step 3  -> sorted list
List<String>sortedList = originalList.stream().sorted().collect(Collectors.toList());

//Now, we use assertion to compare original list vs sorted list (both should look same)
Assert.assertTrue(originalList.equals(sortedList));




//It scans the names in a column with getText (for example, it might find 'Beans'), then prints the price of that specific element using the
// 'getPriceVeggi' method. This method, in turn, creates its own new method where we specify the XPath of the WebElement.

List<String>price= elementsList.stream().filter(s->s.getText().contains("Beans")).map(s->getPriceVeggie(s)).collect(Collectors.toList());
price.forEach(s->System.out.println(s));


}

//A new method has been crafted for "getPriceVeggi," serving its purpose within stream manipulation to identify the specific WebElement among a list of WebElements
private static String getPriceVeggie(WebElement s) {
String pricevalue= s.findElement(By.xpath("following-sibling::td[1]")).getText();
return pricevalue;
}

}

Output

38

Benefits of Custom Selenium Methods with Streams Mapping

Code Readability

Stream mapping promotes concise and readable code, making it easier for developers and testers to understand and maintain automation scripts.

Reusability

Custom methods developed using stream mapping can be reused across different projects, reducing redundancy, and enhancing the efficiency of your automation framework.

Adaptability

Stream mapping allows for the dynamic adaptation of automation scripts to changes in web elements, ensuring the longevity and resilience of your Selenium-based projects.

Conclusion

Incorporating custom Selenium methods through the utilization of stream mapping opens a world of possibilities for web automation enthusiasts. By combining the power of Selenium with the expressive nature of Java stream, developers and testers can create robust, adaptable, and efficient automation scripts. Embrace the flexibility stream mapping offers, and elevate your Selenium automation projects to new heights of sophistication and effectiveness.

Happy Reading!

]]>
https://blogs.perficient.com/2024/02/08/unleashing-the-power-of-custom-selenium-methods-a-guide-to-stream-mapping/feed/ 0 355979
Innovative Technology for an Advanced Security Ecosystem: Challenges and Solutions https://blogs.perficient.com/2023/11/28/innovative-technology-for-an-advanced-security-ecosystem-challenges-and-solutions/ https://blogs.perficient.com/2023/11/28/innovative-technology-for-an-advanced-security-ecosystem-challenges-and-solutions/#comments Tue, 28 Nov 2023 13:45:20 +0000 https://blogs.perficient.com/?p=350244

At the center of digital transformation, we face the exciting challenge of creating an ecosystem driven by high-performance, interconnected microservices developed in diverse languages such as Java, C#, JavaScript, and Python. At Perficient we extract the best of each language to shape an agile and efficient ecosystem. 

Capturing data in real time: IoT and Priority Security   

This dynamic environment is powered by microservices that capture data by integrating with devices through IoT (Internet of Things). The essence of our mission lies in building innovative products aimed to identify external threats, prioritizing the safety of vehicles, passengers, drivers, and pedestrians. Our cutting-edge tools, such as cameras and sensors, offer real-time tracking, supported by AI (artificial intelligence) image recognition.  

Hexagonal Architecture and DevSecOps: Fundamentals of our operation  

The state-of-the-art of our ecosystem is defined by a hexagonal architecture, domain-driven design, and event-driven architecture. This approach ensures that software is built with a ubiquitous language, simplifying functions so any team member can understand them. This results in scalable and flexible software, able to adapt to changing demands and decoupled to offer flexibility in services as the challenge arises. 

The scalability of our system extends to the cloud, working with providers such as Amazon AWS and Microsoft Azure in their commercial and government versions. This facilitates integration with various cloud services, from file storage to serverless services, databases and more, ensuring efficient and effective operation. 

Adopting the DevSecOps culture, we have implemented continuous integration and deployment practices, infrastructure as code and test automation. This combination of practices allows us to consistently add value with a predictable cadence, leading the delivery of advanced technological solutions. 

One of the critical areas of our work focuses on court cases, where we manage the capture, management, and disposition of evidence. This security challenge has led us to comply with rigorous standards such as SOC2, CJIS (criminal justice information) and OWASP-TOP10. 

In the vehicular domain, our systems, connected to the Azure cloud via IoT, search for incident-related license plates in real time. This capability allows immediate action to be taken, contributing to overall safety on the roads.  

Real-time visualization: Complete experience  

Real-time visualization via WebSockets offers a complete experience, displaying travel, vehicle information and all telemetry data. In the case of school buses, this technology ensures children’s safety by stopping and extending the “STOP” paddle, with IoT-connected cameras applying artificial intelligence to detect violations.  

In short, our safety ecosystem represents the convergence of innovation and advanced technology. At Perficient, we move forward into the future with the conviction that technology can transform security and efficiency in our daily lives. We build not just a technology ecosystem, but a safer, more secure and connected future for all. 

]]>
https://blogs.perficient.com/2023/11/28/innovative-technology-for-an-advanced-security-ecosystem-challenges-and-solutions/feed/ 1 350244
Introduction to My Spring Boot Blog Series: Embarking on a Learning Journey Together https://blogs.perficient.com/2023/11/22/introduction-to-my-spring-boot-blog-series-embarking-on-a-learning-journey-together/ https://blogs.perficient.com/2023/11/22/introduction-to-my-spring-boot-blog-series-embarking-on-a-learning-journey-together/#comments Wed, 22 Nov 2023 07:55:41 +0000 https://blogs.perficient.com/?p=349715

Greetings, Fellow Developers, Enthusiasts, and Friends!

I am thrilled to embark on a journey of exploration and learning with you as I dive into the fascinating world of Spring Boot. As I navigate this powerful framework, I’ve decided to document my experiences, insights, and newfound knowledge in a blog series.

The Motivation Behind the Series:

The primary motivation for initiating this blog series stems from a desire to not only solidify my understanding of Spring Boot but also to create a platform for collaborative learning. As I navigate through the various facets of Spring Boot, I aim to provide clear and insightful content that is accessible to beginners, seasoned developers, and anyone else eager to join me on this venture.

Why a Spring Boot Blog Series?

Learning something new is always exciting but sharing that knowledge can make the experience even more rewarding. Spring Boot has captured my attention with its simplicity, productivity, and versatility.  I believe that by chronicling my learning process, I can not only solidify my understanding but also offer a valuable resource for others who are on a similar journey.

What to Expect?

In this blog series, we’ll start from the basics, unraveling the core concepts of Spring Boot. From the initial setup of the development environment to creating RESTful web services and exploring advanced features, we’ll cover it all. Each post will be crafted to provide a clear understanding, complete with practical examples that you can follow along with.

Your Input Matters:

Learning is a collaborative process, and I invite you to be a part of this adventure. Your insights, questions, and suggestions are invaluable to me. Whether you’re a seasoned developer looking to brush up on Spring Boot or someone taking their first steps into Java development, your input will enrich the learning experience for everyone.
Let’s make this a collaborative effort where our collective knowledge becomes a shared resource for everyone involved.

I’m thrilled to introduce the table of contents for our first blog series. In the first installment, we delve into the fundamentals, exploring the essence of Spring Boot.

Table of Contents

Part 1: Getting Started with Spring Boot

  1. Introduction to Spring Boot
    • What is Spring Boot?
    • Key Features and Benefits
    • Spring Boot vs. Traditional Spring
  2. Setting Up Your Development Environment
    • Installing Java Development Kit (JDK)
    • Configuring Integrated Development Environment (IDE)
    • Initializing a Spring Boot Project
  3. Understanding the Basics of Spring Boot
    • The Spring Boot Starter Concept
    • Auto-Configuration in Spring Boot
    • Application Properties and YAML Configuration
  4. Building Your First Spring Boot Application
    • Creating a Simple RESTful Web Service
    • Running and Testing Your Application
    • Project Structure and Configuration Files

Read the first blog here:

Part 1: Getting Started with Spring Boot

I invite you to explore these topics with me, share your insights, and let’s foster a community of collaborative learning.

Let’s Connect

I’m excited to build a community of learners who share a passion for Spring Boot and Java development. Feel free to comment on the blog posts, reach out with questions, or share your own experiences. Let’s create a space where we can learn from each other and grow together.

Stay tuned for the next installment of the series, and let’s embark on this learning journey together.

Happy coding!

]]>
https://blogs.perficient.com/2023/11/22/introduction-to-my-spring-boot-blog-series-embarking-on-a-learning-journey-together/feed/ 1 349715
Spring boot with Java API Client to Build and Execute Queries in Elasticsearch. https://blogs.perficient.com/2022/08/22/elasticsearch-java-api-client-springboot/ https://blogs.perficient.com/2022/08/22/elasticsearch-java-api-client-springboot/#comments Mon, 22 Aug 2022 09:15:03 +0000 https://blogs.perficient.com/?p=316146

Spring boot with ES Java API Client to Build and Execute Queries in Elasticsearch.

Prerequisites:

Knowledge in Java, Spring boot, Elasticsearch, Kibana.

Concept:

The purpose of this blog is to present an idea for connecting, constructing queries, and querying Elasticsearch through Java applications.

What is Elasticsearch?

Elasticsearch is a distributed, free and open search and analytics engine for all types of data, including textual, numerical, geospatial, structured, and unstructured. It is the central component of the Elastic Stack, a collection of free and open tools for data ingestion, enrichment, storage, analysis, and visualization, and is known for its simple REST APIs, distributed nature, speed, and scalability.

Technologies used:

  1. Elasticsearch 8.3.3
  2. Spring boot 2.7.2
  3. Java 1.8
  4. Elasticsearch Java API client 7.17.5
  5. Maven

Tools used:

  1. Kibana 8.3.3
  2. Postman

Note: The blog focuses only on a part of the CRUD operation. Click here for the complete source code with CRUD operations.

 

Project Structure:

Project Structure

Step 1: Create a Spring boot application using Spring Initalizr and select the dependencies as shown in the snapshot below.

Spring Initializr

 

Step 2: Add the additional dependencies given in the pom.xml file below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.poc.es</groupId>
    <artifactId>elasticsearch-springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>elasticsearch-springboot</name>
    <description>Demo project for integrating elasticsearch with springboot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>7.17.5</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.3</version>
        </dependency>

        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
                <version>${project.parent.version}</version>
            </plugin>
        </plugins>
    </build>

</project>

Step 3: Configuring Elasticsearch in Spring boot application.

application.yml

elastic:
  index: employees
es:
  hostname: localhost
  port: 9200
  username: admin
  password: password

ESRestClient.java

package com.poc.es.elasticsearchspringboot.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.Getter;
import lombok.Setter;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
@ConfigurationProperties("es")
@Getter
@Setter
public class ESRestClient {

    private String hostName;
    private int port;
    private String username;
    private String password;

    @Bean
    public ElasticsearchClient getElasticSearchClient() {

        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(username, password));

        RestClientBuilder builder = RestClient.builder(new HttpHost(hostName, port))
                .setHttpClientConfigCallback(httpClientBuilder ->
                        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));

        // Create the low-level client
        RestClient restClient = builder.build();

        // Create the transport with a Jackson mapper
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());

        // And create the API client
        return new ElasticsearchClient(transport);
    }
}

Step 4: Create Rest controller ESRestController.java

@Autowired
private ESService esService;

@PostMapping("/index/fetchWithMust")
public ResponseEntity<List<Employee>> fetchEmployeesWithMustQuery(@RequestBody Employee employeeSearchRequest) throws IOException {
    List<Employee> employees = esService.fetchEmployeesWithMustQuery(employeeSearchRequest);
    return ResponseEntity.ok(employees);
}

@PostMapping("/index/fetchWithShould")
public ResponseEntity<List<Employee>> fetchEmployeesWithShouldQuery(@RequestBody Employee employeeSearchRequest) throws IOException {
    List<Employee> employees = esService.fetchEmployeesWithShouldQuery(employeeSearchRequest);
    return ResponseEntity.ok(employees);
}

Step 5: Create a model Employee.java.

package com.poc.es.elasticsearchspringboot.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    private String gender;
    private String jobTitle;
    private String phone;
    private Integer size;
}

Step 6: Create an interface ESService.java and ESServiceImpl.java.

@Service
public class ESServiceImpl implements ESService {
  
  @Autowired
  private ESClientConnector esClientConnector;

  @Override
  public List<Employee> fetchEmployeesWithMustQuery(Employee employee) throws IOException {
         return esClientConnector.fetchEmployeesWithMustQuery(employee);
  }

  @Override
  public List<Employee> fetchEmployeesWithShouldQuery(Employee employee) throws IOException {
         return esClientConnector.fetchEmployeesWithShouldQuery(employee);
  }
}

Step 7: Create a connector class that makes Elasticsearch API calls ESClientConnector.java.

@Value("${elastic.index}")
private String index;

@Autowired
private ElasticsearchClient elasticsearchClient;   

public List<Employee> fetchEmployeesWithMustQuery(Employee employee) throws IOException {
     List<Query> queries = prepareQueryList(employee);
     SearchResponse<Employee> employeeSearchResponse = elasticsearchClient.search(req->
             req.index(index)
                     .size(employee.getSize())
                     .query(query->
                             query.bool(bool->
                                     bool.must(queries))),
             Employee.class);

     return employeeSearchResponse.hits().hits().stream()
             .map(Hit::source).collect(Collectors.toList());
     }

 public List<Employee> fetchEmployeesWithShouldQuery(Employee employee) throws IOException {
     List<Query> queries = prepareQueryList(employee);
     SearchResponse<Employee> employeeSearchResponse = elasticsearchClient.search(req->
                     req.index(index)
                             .size(employee.getSize())
                             .query(query->
                                     query.bool(bool->
                                             bool.should(queries))),
             Employee.class);

     return employeeSearchResponse.hits().hits().stream()
             .map(Hit::source).collect(Collectors.toList());
     }

 private List<Query> prepareQueryList(Employee employee) {
      Map<String, String> conditionMap = new HashMap<>();
      conditionMap.put("firstName.keyword", employee.getFirstName());
      conditionMap.put("lastName.keyword", employee.getLastName());
      conditionMap.put("gender.keyword", employee.getGender());
      conditionMap.put("jobTitle.keyword", employee.getJobTitle());
      conditionMap.put("phone.keyword", employee.getPhone());
      conditionMap.put("email.keyword", employee.getEmail());

      return conditionMap.entrySet()
                          .stream()
                          .filter(entry->!ObjectUtils.isEmpty(entry.getValue()))
                          .map(entry->QueryBuilderUtils.termQuery(entry.getKey(), entry.getValue()))
                          .collect(Collectors.toList());
     }

 

Step 8: Create Util interface to build ES queries QueryBuilderUtils.java

package com.poc.es.elasticsearchspringboot.utils;

import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryVariant;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;

public interface QueryBuilderUtils {

    public static Query termQuery(String field, String value) {
        QueryVariant queryVariant = new TermQuery.Builder()
                .caseInsensitive(true)
                .field(field).value(value).build();
        return new Query(queryVariant);
    }
}

 

API Calls through Postman:

Fetch data from Elasticsearch using MUST clause:

Fetchwithmust

 

Logs: Constructed ES query with “MUST” clause in Java

POST /employees/_search?typed_keys=true {
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "jobTitle.keyword": {
                            "value": "Senior Developer",
                            "case_insensitive": true
                        }
                    }
                },
                {
                    "term": {
                        "gender.keyword": {
                            "value": "Female",
                            "case_insensitive": true
                        }
                    }
                }
            ]
        }
    }
}

 

 

 

Fetch data from Elasticsearch using SHOULD clause:

Fetchwithshould

 

Logs: Constructed ES query with “SHOULD” clause in Java

POST /employees/_search?typed_keys=true {
    "query": {
        "bool": {
            "should": [
                {
                    "term": {
                        "jobTitle.keyword": {
                            "value": "Senior Developer",
                            "case_insensitive": true
                        }
                    }
                },
                {
                    "term": {
                        "gender.keyword": {
                            "value": "Female",
                            "case_insensitive": true
                        }
                    }
                }
            ]
        }
    }
}

 

 

 

Project Github URL: https://github.com/sundharamurali/elasticsearch-springboot.git

]]>
https://blogs.perficient.com/2022/08/22/elasticsearch-java-api-client-springboot/feed/ 4 316146
Ready, Get Set, Go! on AEM… https://blogs.perficient.com/2022/07/25/ready-get-set-go-on-aem/ https://blogs.perficient.com/2022/07/25/ready-get-set-go-on-aem/#comments Mon, 25 Jul 2022 14:33:50 +0000 https://blogs.perficient.com/?p=295924

AEM has a vast infrastructure, and it requires time to hold expertise on it. Every expert was once a beginner, so you are never too late to start and have fun on AEM. In this blog, we will focus on the developers’ perspective of AEM and how one can start as an AEM developer. We will go through some of the topics which will help you station your pillars on AEM and get the crux of it.

To begin with AEM, you should have a basic understanding of:

  • Java – Servlets, JSP, Collections, OPPs concepts.
  • JS
  • HTML/CSS

Note: This tutorial will focus on the latest AEM – 6.5.

Firstly, What is AEM?

AEM is a Content Management System (CMS) that provides solutions to build websites, mobile apps, and forms. What makes it different from other CMSs is its ability to build content and deliver them in a single platform using various marvelous features such as Dam, Forms, Communities, Cloud, etc.

Note: In this tutorial, we will explore the necessary steps to complete a given functionality. For complete and more details on the related topic, visit the links at the end of this blog.

Go through the following diagram to get a glimpse of the topics we are going to discuss in the blog:

Get Set.drawio Final

So, Let’s begin the groove.

Install AEM on your Local System:

For trying your hands-on, you will need a local AEM instance setup. For that, you will require:

  • AEM quickstart jar and a license file
  • Java 8 or 11 installed in our system

Note: In this tutorial, we are focusing on the 6.5 version of AEM. Recently, it has been the most widely used version with all the available features.

Installing AEM is the first step in, to begin with, AEM. Since we are focusing on the developers’ perspective, we will set up an author instance in our system. Now, to start with, there are two modes in which you can set up your instance:

Author: Instance where you author or design your site. Here you can write your pages, assets, experience fragments, templates, configurations, etc.

Publish: Instance where all the content created in the author instance is deployed after publishing. This is the actual instance where the end user will interact with your site.

Now, let’s set up an author instance, as we will create all our content here, and the developer’s work revolves around this instance.

Follow the steps to set up your instance:

  • Copy and paste the AEM quickstart jar and the license file into a dedicated folder.
  • Rename the jar file to aem-author-p4502.
  • Double-click to run the jar file. It will take around 8-10 mins to run.

After it is done thoroughly, you will notice AEM running on your browser at the 4502 port number, as it is specified in the file name as well(aem-author-p4502). Login with admin/admin, and you are good to go and explore AEM.

Please refer to the link below for more details on setting up your local instance:

https://experienceleague.adobe.com/docs/experience-manager-learn/foundation/development/set-up-a-local-aem-development-environment.html?lang=en

Walkthrough the AEM Environment:

There are many areas in AEM supporting a wide range of functionalities. Go ahead and try your hands on various features.

Welcome Screen Aem

Step into the Author’s shoes

Who is an Author?

The Author is the one who generates content for our pages by authoring and configuring the components. All the blocks of code(components) are combined and integrated by the Author to develop a simple web page. In short, the Author is the mediator between the user and our code.

To develop a component, it is important to understand how the Author will configure the elements on the page and how they will behave and look at the other end.

Please go through the following tutorials to learn authoring pages and components:

Follow this video for an overview of AEM

Go through this document to understand more about authoring in a brief

Editable templates

Once you have understood the authoring pages and components on an AEM page, it’s time to dig deeper and understand pages and their structure.

Any website has a template upon which it is built. Similarly, AEM provides a means by which we can create our templates and edit them dynamically. A template can be created or edited using the template console, as shown in the screenshot below:

Editable Template

Follow the link below to Learn about authoring pages:

https://experienceleague.adobe.com/docs/experience-manager-learn/sites/page-authoring/template-editor-feature-video-use.html?lang=en

After you have created a template, you can create any number of pages from that template, and it will serve as a structure for your page.

This was just an introduction to understanding AEM’s environment and its basic concepts. Being an AEM developer requires a lot more steps. Let’s get into the first few steps that will help you run swiftly.

Time to Develop

Once you are set up in the environment and begin to understand how AEM works as a content management system, it’s time to see how an AEM developer creates custom components and extend the existing component.

Before we move forward, let’s understand a few key concepts used by an AEM developer day in and day out. Following are some of them:

Sightly: This is a scripting language used for rendering the content on the page. Previously, JSP was used for generating the contents. Still, newer versions of AEM sightly became the recommended one for obvious reasons such as code separation, protection against cross-site scripting, etc. Sightly is similar to HTML, with a few new tags helping integrate with AEM concepts.

crx/de Lite: This is a developer console that helps the developer quickly make the changes and verify them on the page. It contains the actual tree structure of the instance organized sophisticatedly. Every node has its purpose of serving. For example, all the components for a project are stored under the app’s node, and all pages are stored under the content node.

Crxde

http://localhost:4502/crx/de/index.jsp

We will learn about more such concepts as we move ahead and explore AEM further.

At this point, you understand how pages are created and authored in AEM and how they serve the content to the end user. Now, let’s understand how these components are designed and how are they made available to the authors.

First, let’s begin by understanding what a component in AEM is?

A component is a working piece of code encapsulated to serve a specific web page area. We can use this component anywhere on our pages and customize its behaviors as required using dialogs.

Creating the First AEM component

For the first component, we will start by reusing the existing component from the we-retail site (Sample site, which is shipped with AEM 6.5).

  • Go to crx/de
  • Navigate through the path apps -> weretail -> components -> content. The components folder contains all the components created for we-retail site. We can further sub-classify them into various categories such as content, structure, etc.
  • Open the button component. You will see the following nodes as shown in the image below:Button Component

For now, let’s focus on button.html and cq:dialog.

button.html: It contains the actual HTML code rendered when the component is added to the page.

cq:dialog: This node forms the node’s dialog, which is served to the Author as an authoring dialog to configure the fields. These values are stored in a node structure under /content node. For example, if you added the button component under men page of we.retail site then these values will be stored under the path /content/we-retail/us/en/men/jcr:content/root/responsivegrid/button_1022012327 as shown in the image below:

Saved Values Aem

Here, I labeled my button as Sample. But, just saving them is not our motive. We have to get these values and render them too. Thankfully, we have Sightly, which makes our job easy. Look at the underlined sentence in button.html in the previous screenshot.

${properties.label}

This is a sightly way of accessing the values from the dialog. So, here properties are implicit objects that AEM provides to access values from a dialog using a dot operator. There are many such implicit objects we can use to get and process the values. They are also known as Global Objects. Please go through Sightly’s documentation to understand more about them.

Here, we are accessing the property/field label. As shown in the screenshot above, the value of the button’s label is stored in the label field. So, we are fetching that value and rendering it on the button using our HTML code. The property’s name is determined by the dialog, as shown below. So whatever name is specified in the dialog, values are stored with that name, and we can access them using the same name.

Button Dialog Aem

You will notice that everything is interconnected. It might be pretty confusing to get it in one go, but as you move ahead, this will be just a piece of cake for you.

Try adding this component to any of the we-retail site pages and experimenting with the component.

Create an AEM Project Using the Latest Archetype

We can experiment and try our hands out in crx/de to understand the functioning of AEM. An AEM developer uses crx/de just for testing, quick fixes, and debugging; hence, we cannot develop the entire project on crx/de. To create any working AEM site, we need to create a project and deploy it in higher environments.

Let’s create an actual AEM project.

Following are the prerequisites for setting up any AEM project: For any AEM project to start working, the following things should be installed in your system:

  1. Maven
  2. Java 8 or higher
  3. Git and Npm
  4. Any IDE, IntelliJ or Eclipse
  5. Adobe Public Maven Repository: This maven2 repository provides access to public artifacts used in conjunction with developing Java-based applications on Adobe frameworks. Just copy the content of this link in your C:\Users\My user.m2\settings.xml directory or /.m2/settings.xml in your mac.

Refer to the following link for more information about this: https://experienceleague.adobe.com/docs/experience-cloud-kcs/kbarticles/KA-17454.html?lang=en

Now, let’s create an AEM project using the maven command.

Go to the local drive location where you want to save your project and open the command prompt from there. Alternatively, you right-click on the desired location on the drive and open Git bash. Now, run the following command:

mvn -B org.apache.maven.plugins:maven-archetype-plugin:3.2.1:generate \
    -D archetypeGroupId=com.adobe.aem \
    -D archetypeArtifactId=aem-project-archetype \
    -D archetypeVersion=35 \
    -D appTitle="WKND Sites Project" \
    -D appId="wknd" \
    -D groupId="com.adobe.aem.guides" \
    -D artifactId="aem-guides-wknd" \
    -D package="com.adobe.aem.guides.wknd" \
    -D version="0.0.1-SNAPSHOT" \
    -D aemVersion="cloud"

As soon as the command finishes its execution, you will see a project being created at the desired location.

This was a straightforward way to kickstart any project. Once you have got a hold of AEM, this is the only step required to create and begin on any project, but since you are a beginner, please go through the following document and understand the project structure and modules of any AEM project.

https://experienceleague.adobe.com/docs/experience-manager-learn/getting-started-wknd-tutorial-develop/project-archetype/project-setup.html?lang=en

Please run the following command to install the project on your local instance:

mvn clean install -PautoInstallPackage -PautoInstallBundle

Please go to the following link if you want to understand more about the command:

https://experienceleague.adobe.com/docs/experience-manager-core-components/using/developing/archetype/overview.html?lang=en

Once the project has been built successfully, open the project in your IDE(IntelliJ or Eclipse)

To enable your IDE to understand AEM modules and concepts, you need to install plugins in your IDE.

Eclipse: Install AEM Developer Tools for Eclipse. Follow the link for a step-by-step procedure – https://experienceleague.adobe.com/docs/experience-manager-64/developing/devtools/aem-eclipse.html?lang=en

IntelliJ: Install AEM IDE Tooling 4 IntelliJ. Just go to File -> Settings -> Plugins in your IDE. Search the plugin and install it.

These plugins provide an easy way to manage and develop any AEM project by strengthening the features IDE provides. It is always good to have these plugins installed in your IDE for faster development and effortless mappings.

Creating a Custom Component

AEM allows you to create custom components and extend the existing core component, which is shipped with AEM. Here, we will see how to create custom components and concepts such as sightly, sling models, and dialogs.

Please follow the following tutorial on how to create a custom component:

https://experienceleague.adobe.com/docs/experience-manager-learn/getting-started-wknd-tutorial-develop/project-archetype/custom-component.html?lang=en

Note: The tutorial refers to another git repository, but you can also create this component in your newly created project.

While going through the tutorial, you must have come across a few new concepts like sightly and sling models. You don’t need to understand them in one go. So, no worries, you will get them as you work with them more. Again, AEM is not a very simple thing to understand, but once you start, there is no looking back.

To understand more about the new concepts, refer to the following links:

Sling Models

Sightly

As you start understanding these concepts, everything will make sense, and you will be able to match the pieces together.

Many More To Go

These were just baby steps to an understanding of AEM and beginning with it. AEM is not, that’s all. We haven’t even explored 10% of it. But the concepts we went through earlier will help you get hold of AEM so that you can explore further on it. It was ABC for AEM. To name a few, the following are a few more concepts that will help you understand and develop a site in AEM:

So, Don’t Wait Up. Get Set and Go Ahead on AEM…

]]>
https://blogs.perficient.com/2022/07/25/ready-get-set-go-on-aem/feed/ 2 295924
Implementation of Completable Future of Java 8 https://blogs.perficient.com/2022/07/15/implementation-of-completable-future-of-java-8/ https://blogs.perficient.com/2022/07/15/implementation-of-completable-future-of-java-8/#respond Fri, 15 Jul 2022 12:29:45 +0000 https://blogs.perficient.com/?p=313732

Completable-Future is used for asynchronous programming in Java. Asynchronous programming means writing non-blocking code by running a task on a separate thread than the main application thread and notifying the main thread about its progress, completion or failure.

This way, main thread does not block/wait for the completion of the task and it can execute other tasks in parallel.

Having this kind of parallelism greatly improves the performance of your programs.

CompletableFuture is an extension to Java’s Future API which was introduced in Java 5.

A Future is used as a reference to the result of an asynchronous computation. It provides an isDone() method to check whether the computation is done or not, and a get() method to retrieve the result of the computation when it is done.

SpringBoot APIs can leverage concurrency in its business logic using Async annotations and java.util.concurrent.CompletableFuture class. This would enable the application to run the annotated methods in a separate thread pool, asynchronously whenever called. Main thread that makes the call would not be blocked for each of those calls. How do we enable it and use it in a sample API? Read on.

The key steps behind enabling Async methods in Spring are here:

  1. Enable async processing in Spring Boot by annotation Spring Boot Application class with @EnableAsync.

 

AsyncConfiguration.java

 

@Configuration

@EnableAsync

public class AsynchConfiguration

{

@Bean(name = “asyncExecutor”)

public Executor asyncExecutor()

{

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setCorePoolSize(3);

executor.setMaxPoolSize(3);

executor.setQueueCapacity(100);

executor.setThreadNamePrefix(“AsynchThread-“);

executor.initialize();

return  executor;

}

}

 

2.  Create a ThreadPoolExecutor to run async methods in separate threads.

@Configuration

@EnableAsync

public class AsyncMethodConfigurer implements AsyncConfigurer{

@Bean

public Executor taskExecutor() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setCorePoolSize(4);

executor.setMaxPoolSize(5);

executor.setQueueCapacity(500);

executor.setThreadNamePrefix(“AsyncGetUserInfo-“);

executor.initialize();

return executor;

}

3.  Annotate the methods that need to be run in separate threads with @Async.

@Async(“asyncExecutor”)

public CompletableFuture<EmployeeNames> methodOne() throws InterruptedException {

//code

}

4.  keep method return type wrapped into: CompletableFuture <T>

@Async

public CompletableFuture<User> findUser(String user)   {

5.  return the result as: pass exception block along. your try{ }catch would not work here.

return cmpletableFuture.completedFuture(results).exceptionally((throwable   -> {

logger.error(” “+throwable);

return null;

}));

6.  Handler sample :-

@RequestMapping(value = “/async-poc/userinfo”, method = RequestMethod.GET)

@ResponseBody

public List<User> getAsyncUserInfo(@RequestBody List<User> userList) throws                     InterruptedException, ExecutionException {

List<User> responseList =new LinkedList<>();

//Step 1: Collect input list for async methods

List<String> userNameList = userList.stream().map(User::getName)

.collect(Collectors.toList());

//Step 2: invoke async methods

List<CompletableFuture> futuresList   =userNameList.stream().map(asyncUserInfoService::findUser)

.collect(Collectors.toList());

//Step 3: Get all results and Combine to Send back HTTP Response Entity

futuresList.stream().map(CompletableFuture::join).collect(Collectors.toList());

for (CompletableFuture completableFuture : futuresList) {

try {

log.info(“– > “+completableFuture.get());

responseList.add((User)completableFuture.get());

} catch (InterruptedException e) {

log.error(ex.getMessage());

} catch (ExecutionException e) {

log.error(ex.getMessage());

} catch (Exception e) {

log.error(ex.getMessage());

}

}

return responseList;

}

 

The key classes/methods used from java concurrency package:-

  1. CompletableFuture – Class that we will be using mainly here.
  2. exceptionally() ,handle(), whenComplete() – called after completion of your method. keep any error handling here.
    1. note: handle() and whenComplete have access to completable future’s success result (T) and failure (Throwable) as input arguments. On the other hand, method exceptionally() only has access to failure as an input argument.
  3. allOf (*) – Returns a new CF that is completed when all of the given CFs complete. Used by the main thread to wait until the async threads passed in, are completed and move forward with processing other things.
  4. join() – to receive the result of each method call. your actual return object that was wrapped in CF before.

REFERENCES:

  1.     spring.io/guides

2.CompletableFuture (Java SE 9 & JDK 9 )

]]>
https://blogs.perficient.com/2022/07/15/implementation-of-completable-future-of-java-8/feed/ 0 313732
Introduction to Spring Framework for Java https://blogs.perficient.com/2022/01/20/introduction-to-spring-framework/ https://blogs.perficient.com/2022/01/20/introduction-to-spring-framework/#respond Thu, 20 Jan 2022 19:58:16 +0000 https://blogs.perficient.com/?p=303626

Introduction to Spring Framework for Java

This article walks through the basics of using Spring Framework for Java.

From a very, very high level point of view, Spring Framework infers a program’s runtime behavior from labels that the programmer attaches to pieces of code. There are many different groupings of labels, and each grouping of labels provides an interface to the configuration of some behind-the-scenes process.

Since a simple-looking Spring Framework program with just a few labels can have quite a lot going on behind the scenes, learning Spring Framework can seem a little overwhelming to the beginner. Learning is made even more difficult by the fact that most online resources documenting Spring Framework haphazardly walk through different types of labels instead of building a fundamental ground-up understanding.

This article intends to fill this gap and provide a ground up understanding. We will start with vanilla Java, and then, one programming design pattern at a time, we will augment on an understanding of how that design pattern is configured in Spring Framework with labels.

Before you begin reading this article in earnest…

  • You should have a firm grasp of how object oriented programming is achieved in Java. So, you should be familiar with concepts such as: “pass reference by value” and “pass by reference”, classes, constructors, fields, access modifiers (public and private), methods, static methods, class instances/objects, getter and setter methods, inheritance, method overloading, runtime polymorphism, interfaces and abstract methods, etc.
  • You should know about the basics of Java annotations. The “labels” spoken of above are really annotations.

  • You should be aware of Macro Behler’s excellent introduction to Spring Framework. I’ve mentioned that most online resources on Spring Framework are disorganized and disappointing; his article is one of the few exceptions. It’s always good to have multiple readings to pull from when learning a topic, and so I encourage you to read this article as a supplement to this.

Dependency injection

Above, our “very, very high level” point of view was that Spring Framework infers a program’s runtime behavior from Java annotations. This is an accurate surface-level description of Spring Framework, but it isn’t a good characterization from which to grow a fundamental understanding.

We will begin our understanding of Spring Framework with a different characterization. At its core, Spring Framework is a tool for implementing the design pattern called dependency injection.

In dependency injection, the object instances on which a Java class Cls depends are “injected” into an instance obj of Cls by a container that has a reference to obj. Since the container, rather than obj, controls when obj‘s dependencies are injected, it is often referred to as an inversion of control container. Dependency injection is also sometimes called “inversion of control” for this reason.

What’s the point of dependency injection? Well, one of the advantages is that it allows us to avoid instantiating unnecessary copies of a dependency.

Suppose that multiple classes require a reference to an object that represents a connection to a particular database. Since this reference is a dependency, we can easily share a single database connection among all of the class instances by making use of the dependency injection technique described above. This is much better than wastefully giving each class instance its own database connection.

Spring beans

When using Spring Framework, we will spend most of our time dealing with and thinking about “Spring beans,” which are the dependencies that are managed by the Spring IoC container.

To be more specific, a Spring bean is a not-necessarily-Serializable object that…

  • is created at runtime by the Spring IoC container (IoC stands for inversion of control)

  • has references to other objects or beans (“dependencies”) injected into it at runtime by the Spring IoC container

  • is otherwise controlled at runtime by the Spring IoC container.

Spring beans can be configured via XML files or by using Java annotations within Java classes. Using annotations is the modern approach. This article will use annotations only- no XML code.

Note that a Spring bean is different than a JavaBean. JavaBeans are part of the core Java language, while Spring beans are obviously not. (Specifically, a JavaBean is a Serializable class with a public no argument constructor that has all fields private. JavaBeans are also not managed by the Spring IoC container).

Configuring Spring beans

In the above, we said that Spring beans are created at runtime by the Spring IoC container. But how does the Spring IoC container know what sort of beans to create? Well, you, the programmer, must write “configuration code” that specifies which Java classes should be instantiated as Spring beans at runtime.

There are two main ways to do this: either use the @Bean annotation or the @Component annotation. (You can also use an annotation derived from @Component).

First configuration method: @Beaned methods that return instances

To specify that Cls should be instantiated as a bean at runtime, annotate a method that returns an instance of Cls with @Bean, and place that annotated method within a class that is itself annotated with @Configuration:

@Configuration
public class Config
{
     @Bean
     public Cls createABeanWrappingAClsInstance(Object args) { return new Cls(args); }
}

Second configuration method: use @Component and @ComponentScan

Another way to specify that a class Cls should be instantiated as a bean at runtime is to annotate Cls‘s class declaration (i.e. public class Cls) with @Component, while also annotating some other class, say Config, that is “at or above below” the level of Cls in the directory hierarchy with @Configuration and @ComponentScan:

@Configuration
@ComponentScan
/* Config must be at or above Cls in the directory hierarchy. */
public class Config { }

@Component
public class Cls { ... }

(When you create later create an ApplicationContext in the main() method of your application, you will have to pass ApplicationContext.class to the ApplicationContext constructor. But, if you are using Spring Boot, the class containing the main() method is already secretly annotated with @Configuration and @ComponentScan, so you don’t need to do anything other than annotate Cls with @Component). 

Specifically, using @ComponentScan in this way specifies that, at runtime, if a class “at or below” the level of Cls in the directory hierarchy is annotated by @Component  or by an annotation whose parent annotation* is @Component, then that class will be used to construct a Spring bean. Notably, the annotations @Service, @Repository, and @Controller all have @Component as a parent annotation.

*One annotation is considered to be the “child annotation” of another annotation if it is meta-annotated by that other annotation.

Sidenote: annotation “inheritance” in Spring

As is noted in this Stack Overflow answer, Spring Framework’s AnnotationUtils class has a method that tests whether an annotation is equal to or is annotated with another annotation. I’m making an educated guess that Spring uses this sort of inheritance testing for annotations all over the place.

Differences between @Service, @Repository, and @Controller

@Service, @Repository, and @Controller are similar in that they are child annotations of @Component (i.e. they are all meta-annotated by @Component). What are some differences?

  • @Service indicates to the programmer that the class it annotates contains “business logic”. Other than that, it doesn’t enable any behind-the-scenes behavior. The Spring devs may change this some day.

  • @Repository “is a marker for any class that fulfils the role or stereotype of a repository (also known as Data Access Object or DAO). Among the uses of this marker is the automatic translation of exceptions [from implementation exceptions to a Spring exception]” (from here).

  • @Controller must annotate a class if we want to use annotations from Spring Web MVC of the form @<request type>Mapping. These annotations are used for setting up HTTP API endpoints.

Extra readings: @Component vs. @Service vs. @Repository vs. @Controller, @Component vs. @Bean.

Implementing dependency injection

We now know how to configure Spring beans, but don’t yet know anything about how to actually dependency-inject Spring beans into other Spring beans. We describe how to do so in this section.

Though, before we describe how to do so, there is a little more prerequisite knowledge we should cover.

More prerequisite knowledge

Convention: “bean definition”

For the rest of this document, the term bean definition will refer to a method annotated with @Bean that returns an object instance or a class annotated with @Component.

Bean scopes

Every bean definition has an associated “scope”.

The default (and most important) scope is singleton. If a bean is of singleton scope, all references to that bean access the same Java object. singleton scope is used to achieve dependency sharing, which, if you recall the above “Dependency injection” section, is one of the key advantages of using an IoC container.

The second most important scope is prototype. If a bean is of prototype scope, different references to that bean are references to different Java objects.

The four other scopes, request, session, application, and websocket, can only be used in a “web-aware application context,” and are less commonly used. Don’t worry about these ones.

Terminology: “plain old Java objects” (“POJOs”)

A “plain old Java class” is a class that does not depend on an application framework such as Spring. Basically, since most Spring features are handled with annotations, a plain old Java class is a class without any Spring annotations.

Unfortunately, people say “plain old Java object” instead of “plain old Java class”, so we speak of POJOs instead of POJCs.

POJOs are often used in Spring apps in combination with not-POJOs to represent “more concrete” objects (such as an Employee, etc.).

Extra reading: http://www.shaunabram.com/beans-vs-pojos/.

Implementing somewhat-manual dependency injection

Now, we are actually ready to use Spring Framework to implement the dependency injection design pattern.

Suppose we’ve configured a Spring bean named Cls1 that has a reference to a Spring bean Cls2:

@Component
public class Cls1
{
     private Cls2 cls2;
     public Cls2 getCls2() { return cls2; }
     public void setCls2(Cls2 cls2) { this.cls2 = cls2;}
}
​
@Component
public class Cls2 { ... }

We want to inject an instance of the Cls2 into our Cls1 bean at runtime. To do so, we need a reference to the Spring IoC container.

The interfaces BeanFactory and ApplicationContext both represent the IoC container. Since ApplicationContext extends BeanFactory, and therefore has more functionality, ApplicationContext should be used in most situations.

We perform dependency injection by using ApplicationContext as follows:

public class Application
{
   public static void main(String[] args)
  {
       /* <package> is the package inside which to look for @Configuration classes and in which to perform @ComponentScan. For example,    
       <package> might be "com.perficient.techbootcamp.*" */
       ApplicationContext ctx = new annotationConfigApplicationContext("<package>");
       
       /* The below assumes that a class named Cls has been configured as a bean (recall, this is done
       by using @Component and @ComponentScan or by using @Bean). */
       Cls1 cls1 = ctx.getBean(Cls1.class);
       
       /* Perform dependency injection: inject an instance of cls2 into the bean cls1. */
       Cls2 cls2 = new Cls2();
       cls1.setCls2(cls2);
  }
}

The above code is adapted from Macro Behler’s article.

Implementing dependency injection with @Autowired

In Spring Framework, one typically uses annotations that execute the effect of the above dependency injection behind the scenes. Specifically, one uses the @Autowired annotation. When @Autowired is present on a bean’s field, an instance of that field’s type will be injected into that field at runtime.

So, if we want to replicate the functionality of the above, we would write the following:

@Component
public class Cls2 { ... }
​
@Component
public class Cls1
{
     @Autowired
     private Cls2 cls2;

     public Cls2 getCls2() { return cls2; }
     // Notice, no setter necessary.
}
​
public class Application
{
     public static void main(String[] args)
     {     
           ApplicationContext ctx = new annotationConfigApplicationContext("<package>");
       
           /* The below code has been commented out because it is unnecessary.
              The above @Autowired annotation tells Spring Framework to inject
              a reference to the Cls2 bean into the Cls1 bean at runtime. */
       
           // Cls1 cls1 = ctx.getBean(Cls1.class);
           // Cls2 cls2 = new Cls2();
           // cls1.setCls2(cls2);
   }
}

Field injection with @Autowired

You may wonder how it is possible to inject an instance of Cls2 into cls1 when Cls1 has no setCls2() method. After thinking about it for a second, you might suspect that injection is done by using Cls1‘s constructor. This is actually not the case. (In the above code, Cls1 doesn’t have a with-args constructor!). When @Autowired annotates a bean’s field, then, at runtime, the IoC container uses this Java reflection technique to modify the field, even if it’s private.

Placing @Autowired on a field thus constitutes field injection.

Using @Autowired on fields is bad practice

According to this article, using field injection is bad practice because it disallows you from marking fields as final . (You want to be able to mark fields as final when appropriate because doing so prevents you from getting into a circular dependency situation).

More reasons why field injection is bad: https://dzone.com/articles/spring-di-patterns-the-good-the-bad-and-the-ugly.

Using @Autowired on constructors and setters

@Autowired can also be used on constructors or setters to inject a parameter into a constructor or setter at runtime.

The @Qualifier annotation

Because a bean could have an @Autowired field whose type is an interface, and because multiple classes may implement the same interface, it can be necessary to specify which implementation of the interface is meant to be dependency-injected. This is done with the @Qualifier annotation, as follows:

public interface Intf { ... }

@Qualifier("impl1")
@Component
public class Impl1 extends Intf { ... }
​
@Qualifier("impl2")
@Component
public class Impl2 extends Intf { ... }
​
public class Cls
{
     @Autowired
     @Qualifier("impl1")
     private Cls1 cls1Instance; // at runtime, cls1Instance will be set to a Cls1 instance
   
     @Autowired
     @Qualifier("impl2")
     private Cls2 cls2Instance; // at runtime, cls1Instance will be set to a Cls1 instance
}

Here are the specifics of how field-names are matched to bean-names:

  • Define the qualifier-name of a bean definition or field to be: (1) the argument of the @Qualifier annotation attached to said bean definition or field, if the bean definition or field is indeed annotated with @Qualifier, and (2) the name of the class associated with the bean definition, if the bean definition or field is not annotated with @Qualifier.

  • When no @Qualifier annotation is present on a field, then the class whose case-agnostic qualifier name is equal to the case-agnostic name of the field is what is dependency-injected into the field. (“Case agnostic” means “ignore case”).

End

This concludes my introduction to Spring Framework for Java. I hope you’ve gained a sense as to how Spring Framework allows you to implement dependency injection!

]]>
https://blogs.perficient.com/2022/01/20/introduction-to-spring-framework/feed/ 0 303626