GCast 135:

Deploying a Web App to Azure from VS Code

Visual Studio Code is an excellent development environment for creating web applications. It also contains tools to deploy those applications to Azure App Services. Learn how to use those tools to move your web app to the cloud.


Using the MS Graph API

Comments [0]

Microsoft Graph (MS Graph) provides a REST API that allows you to access and manage many Active Directory (AD) objects, including information about users and their organizations. Microsoft Graph provides an API that allows you to read and write objects in Azure and Microsoft 365 objects. Examples include accessing and maintaining information on users, calendars, Teams, and devices.

You can call the Graph API by sending a POST, PUT, or GET request to a set of endpoints at https://graph.microsoft.com. As of this writing, MS Graph is on version 1.0, so all the endpoints begin with https://graph.microsoft.com/v1.0/, but that version number at the end may change in the future. These endpoints are documented here.

Some of these requests require sending some data in the HTTP body and all of them require sending data in the HTTP header. One common method of authentication is to send a Bearer Token in the header of each HTTP request. A JSON Web Token (JWT) is a type of Bearer Token issued by an identity provider, such as Azure Active Directory (AAD). It verifies the identity of the person or service making the API call, and it can contain information about that identity. In order to successfully, generate a JWT, you must create a user account; register an application with Azure Active Directory; and must generate a Client Secret; and you must grant the appropriate Microsoft Graph permissions to the account.

An API call runs as an account identity. That identity must be authenticated by someone that MS Graph trusts. That identity must have permission to perform actions in the API call

Another requirement of making a call to the Microsoft Graph API is that the caller must have permission to perform the requested actions on the objects specified in the request. For example, in order to read information about a user, the account must have READ permissions on the User object.

Fig. 1 illustrates the prerequisites to making a Graph API call.

Workflow to call MS Graph
Fig. 1

Below are the steps (including prerequisites) when making a call to MS Graph. For each prerequisite, I have linked to an article describing the step in detail.

  1. Register App. Record Tenant ID and Client ID
  2. Create Client Secret
  3. Obtain appropriate AD permissions
  4. Create Token
  5. Call API

Once you have a token representing a user account and that account has the appropriate permissions, you can make a call to the Microsoft Graph API.

Let's start with a call to get all users. Send an HTTP GET request to https://graph.microsoft.com/v1.0/users.

Add the following key-value pair to the HTTP Header of the request:

key value
Authorization Bearer jwt

where jwt is the JSON Web Token you created for the user account.

This is shown in Fig. 2.

Calling MS Graph User API with Postman
Fig. 2

As you can see, if we pass a valid JWT, the API returns a list of users in JSON format.

In this article, I introduced Identity Management and the Microsoft Graph API; I showed how they work together, and I provided step-by-step instructions on implementing IAM to access the API.


Episode 726

Christina Aldan on Brain Performance and Memory

Christina Aldan discusses the physiology of brain performance and memory; and describes things we can do to improve our memory.


"The Path of Daggers" begins the second half of Robert Jordan's 14-volume "Wheel of Time" series.

The book progresses the story: a group of heroines use an ancient artifact to reverse the unnatural global warming brought on by demonic forces of darkness in a previous novel; and Elayne finally returns to Caemlyn to ascend to the throne, vacated by her mother, who disappeared two books ago; Rand - the "Dragon Reborn" battles an invading army and fights to retain his sanity which is eroded by his use of mystical powers.

It is not a bad book in a decent series, but it has already gone on longer than needed.

"Daggers" is the shortest in the series and the first to reach #1 on the New York Times Best Seller list.

One would think Jordan would learn his lesson from this and begin writing shorter novels.

Alas, this was not the case. Jordan continued to plod along, slogging through his story. On to volume 9!

 


"In the narrative of my life, which is the look backward rather than forward into the unknown and unstoried future, I emerged from the pool as from a baptismal font—changed, reborn—as if I had been shown what would be my calling even then. This is how the past fits into the narrative of our lives, gives meaning and purpose. Even my mother’s death is redeemed in the story of my calling, made meaningful rather than merely senseless. It is the story I tell myself to survive."

Natasha Trethewey is a poet. A very good one. So good, in fact that she was named Poet Laureate of the United States in 2012.

But "Memorial Drive" is not a book of poetry. It is a memoir of her life, written in prose, rather than poetry. Yet even her prose sounds like poetry. In describing a photograph of her grandmother holding Natasha's infant mother in her arms, she writes:

"But the photograph hints, too, at another story. I can see it in the tall grass brushing her ankles, the blades bent as if moved by wind."

Trethewey was born in Mississippi on the 100th celebration of Confederate Memorial Day - a day which celebrates a battle for white supremacy in the south. As the daughter of a white Canadian father and a black southern mother, she grew up experiencing racism firsthand. Interracial marriage was illegal in Mississippi at the time of their matrimony!

But this book is less about her early childhood in Mississippi and more about the time after her parents’ divorce, when she and her mother emigrated to Atlanta. She recounts the abuse suffered by her mother at the hands of her stepfather Joel - abuse that ultimately led to the murder of her mother. She refers to this period as her "lost years".

Ms. Trethewey pours out her soul into these writings, recounting memories she had tried to forget.

"You are in the fifth grade the first time you hear your mother begin beaten", she writes.

Memorable parts of the book include:

  • Upon discovering that her stepfather had broken the lock on her journal and was reading it, teenage Natasha began addressing journal entries directly to him, letting him know he was abusing her mother.
  • The book reprints parts of a journal kept by Natasha's mother in the weeks before she died. The police found it in her briefcase the day of the murder, but Natasha did not read it until 25 years later. It details the abuse she suffered during her second marriage.
  • A police report to the police, following Joel's first attempt on the life of his estranged wife.
  • An encounter decades after the incident with a policeman who was on the scene. Meeting Ms. Trethewey brought tears to the eyes of the officer.

Brace yourself for an emotional journey - one you will not soon forget.


In a previous article, I showed you how to create an Active Directory user account.

Accounts are not much use unless they can do something, such as create, read, update, or delete objects. In order for a user account to do anything, that account must have permissions to perform the attempted action. A user can grant permissions to some objects associated with themselves. For example, I may give you permission to read my calendar; but, for most objects and actions, an AD Administrator is responsible for granting those permissions. Here are the steps to take while logged into Azure as an AD Administrator for the subscription involved. This article describes how to grant access to add permission to read user accounts (which can be done via Microsoft Graph), but the steps are similar regardless what permissions you are granting.

Log into the Azure Portal as an Administrator and search for Azure Active Directory, as shown in Fig. 1 to display the Azure Active Directory "Overview" blade, as shown in Fig. 2.

Search Azure for Active Directory
Fig. 1

Azure Active Directory Overview Blade
Fig. 2

Click the [App registrations] button (Fig. 3) to open the "App Registrations" blade, as shown in Fig. 4.

Azure Active Directory Overview Blade
Fig. 3

App Registrations Button
Fig. 4

Select your App registration to open its details page, as shown in Fig. 5.

App Registrations Blade
Fig. 5

Click the [API permissions] button (Fig. 6) in the left menu to display the "API permissions" blade, as shown in Fig. 7.

Registration Details Page
Fig. 6

API Permissions Blade
Fig. 7

Click the [Add a permission] button (Fig. 8) to display the "Request API permissions" dialogue, as shown in Fig. 9.

AddPermissionButton
Fig. 8

Request API Permissions Dialogue
Fig. 9

Click the "Microsoft Graph" button (Fig. 10) to filter the permission request to Microsoft Graph permission types, as shown in Fig. 11.

MS Graph Button
Fig. 10

MS Graph Permissions Filtered Dialogue
Fig. 11

Click the [Application permissions] button (Fig. 12). This allows the account to make calls, even if it is not explicitly signed in to AD. A list of permission categories displays, as shown in Fig. 13.

Application Permissions Button
Fig. 12

List of permissions
Fig. 13

Here you can expand a category and select the permissions to grant to an account via this Application Registration. Fig. 14 shows the "User" category expanded with the "User.ReadAll" selected, which would allow an account to Read all users' information in the Active Directory, but would not allow them to change, add, or delete user information.

User Permissions
Fig. 14

Click the [Add permissions] button (Fig. 15) to save selected permission changes.

Add Permissions Button
Fig. 15

 

Configured Permissions
Fig. 16

The Configured Permissions list displays again with the _User.Read.All_ application permission added. Note the "Yes" under the "Admin consent required column" and the "Not granted" warning in the "Status" column. This indicates that the permission will not take effect until an admin consents to it. Not all permissions require this, but _User.Read.All_ does. Click the [Grant admin consent" button (Fig. 17) and click the [Yes] button when prompted for confirmation (Fig. 18).

Grant Admin Consent Button
Fig. 17 Grant Admin Consent Confirmation
Fig. 18

The Configured Permissions list displays again with the warning replaced by a "Granted" message in the "Status" column, as shown in Fig. 19. The account now has permission to read user information.

Configured Permissions
Fig. 19

As you can see, the steps for granting permissions to an Active Directory User Account are simple. The catch is that you must be logged on as the owner of the object acted upon or as an AD Administrator in order to grant these permissions.


Creating a JWT Bearer Token

Comments [0]

A Bearer Token is a token that can be sent with a web request. It is an encrypted string that indicates under which account the request will run, validates the authenticity of that request, and (potentially) contains some information about that account.

Microsoft Azure can generate a JSON Web Tokens (JWT) when it validates an account. A JWT can be used as a Bearer Token.

This article describes how to generate a JWT.

In order to successfully generate a JWT, you must register an application with Azure Active Directory (described in this article); generate a Client Secret (described in this article); and you must grant the appropriate Microsoft Graph permissions to the account (described in this article).

You will need the following information from the Application Registration step:

-Tenant ID

-Client ID

-Client Secret

To generate a new JWT, send an HTTP POST request to https://login.microsoftonline.com/{{TenantID}}/oauth2/v2.0/token

where {{TenantID}} is the Tenant ID of the Azure subscription recorded above.

You can do this in code or using a CURL command or using a tool like Postman, I will demonstrate how to make this API call using Postman.

In Postman, create a new request, select POST from the HTTP Verb dropdown, and enter into the URL textbox "https://login.microsoftonline.com/{{TenantID}}/oauth2/v2.0/token", replacing {{TenantID}} with the Tenant ID recorded above.

This is shown in Fig. 1.

API URL in Postman
Fig. 1

Select the "Body" tab, select the "x-www-form-urlencoded" radio button, and enter the following Key-Value pairs:

Key Value
client_id (the Client ID recorded above)
client_secret (the Client Secret recorded above)
scope https://graph.microsoft.com/.default
grant_type client_credentials

This is shown in Fig. 2.

Request Body in Postman
Fig. 2

Click the [Send] button to send the HTTP POST request. You should receive a response, similar to the one in Fig. 3.

Response Body in Postman
Fig. 3

The resulting JSON includes a node named "access_token". Copy and save this value (without the surrounding quotation marks). You will need it later.

The access token is an encrypted JSON object. You can view this object by pasting it into the form at https://jwt.ms, as shown in Fig. 4.

Decoding the JWT with jwt.ms
Fig. 4

Now that you have a JWT, you can pass this in the header of an HTTPS request to identify to the API which account is making the request.


Creating a Service Principle in Azure requires a Client Secret. The Secret serves as the password for the principle. For example, you will need a Client Secret in order to generate a JSON Web Token.

A Client Secret is associated with an Application Registration. To get started, you will need to register your application in Active Directory, as described in this article.

Log into the Azure Portal and search for Azure Active Directory, shown in Fig. 1.

Search Azure for Active Directory
Fig. 1

Select "Azure Active Directory" to navigate to the Azure Active Directory "Overview" blade, as shown in Fig. 2.

Azure Active Directory Overview Blade
Fig. 2

Click the [App registrations] button (Fig. 3) to open the "App Registrations" blade, as shown in Fig. 4.

App Registrations Button
Fig. 3

App Registrations Blade
Fig. 4

Select your App registration to open its details page, as shown in Fig. 5.

Registration Details Page
Fig. 5

Click the [Certificates & secrets] button (Fig. 6) to display the "Certificates & secrets" blade, as shown in Fig. 7.

Certificates And Secrets Button
Fig. 6

Certificates And Secrets Blade
Fig. 7

Select the "Client secrets" tab, if it is not yet selected.

Click the [New client secret] button (Fig. 8) to display the "Add client secret" dialogue, as shown in Fig. 9.

New Client Secret Button
Fig. 8

Add Client SecretDialogue
Fig. 9

Provide a brief description of the secret. This will show up in lists, making it easier to identify later.

Select a time at which the secret will expire and need to be regenerated.

Click the [Add] button to generate the Client Secret and return to the "Certificates & Secrets" value. You should see your newly-generated secret listed on this blade. Copy and save the "Value". You will need it later.

IMPORTANT:

After you navigate away from this page, there is no way to retrieve the Secret Value. If you do not copy and save it now, you will need to regenerate a Secret. 

Keep this secret in a safe place - in Azure Key Vault or in a secure folder. If it is compromised, a hacker can access your API with this service identity.

This article showed how to generate a Client Secret. This Client Secret serves as a password for a principle account. You can use it when generating a JSON Web Token.


Overview

You may want to add a new user to an Azure Active Directory to onboard a new employee, to allow an external person access to the directory or to provide an account under which a service will run.

Creating a new user in Azure Active Directory is a straightforward process and can be done within the Azure Portal.

The Steps

Log into the Azure Portal as an Administrator and search for Azure Active Directory, as shown in Fig. 1 to display the Azure Active Directory "Overview" blade, as shown in Fig. 2.

Search Azure for Active Directory
Fig. 1

Azure Active Directory Overview Blade
Fig. 2

Click the [User] button (Fig. 3) in the left menu to display the "Users" blade, as shown in Fig. 4

Users Button
Fig. 3

Users Blade
Fig. 4

Click the [New User] button and select "Create new user" from the dropdown, as shown in Fig. 5.

New User Button
Fig. 5

The "New user" dialog displays, as shown in Fig. 6.

New User Dialogue
Fig. 6

At the "User name" field, enter a unique name for this user. This will be displayed in user lists and will be used to sign in and potentially as an email address.

At the "Name" field, enter the full name of the user.

At the "First name" field, enter the user's first name

At the "Last name" field, enter the user's last name

At the "Groups and roles" field, select any AD Groups or Roles to which you wish to assign the user.

At the "Block sign in" toggle, select "Yes" if you want to wait to make the user active; otherwise select "No" (the default).

At the "Usage Location" field, select the user's primary country of residence.

At the "Job title" field, enter the user's title.

At the "Department" field, enter the user's department.

At the "Company name" field, enter the name of the company for which the user works.

At the "Manager" field, you may select the user's manager.

"User Name" and "Name" are the only required fields in this dialog. Almost all of these settings can be added or changed later.

Click the [Create] button to create this user.

You will return to the "Users" blade, where you can use the search box to find the user you just added, as shown inf Fig. 7.

Search For User
Fig. 7

Click the Display name link to display details about the user, as shown in Fig. 8.

User Details
Fig. 8

Modifying a User

To modify information about the user, click the [Edit properties] (Fig. 9) button to display the Properties dialog, as shown in Fig. 10.

Edit Properties Button
Fig. 9

Properties Dialogue
Fig. 10

To delete a user, click the [Delete] button (Fig. 11); then click the [Delete] button in the confirmation dialog shown in Fig. 12.

Delete Button
Fig. 11

Delete Confirmation Dialog
Fig. 12

As you can see, there are far more user properties to edit than were available when you created the user. Make any desired changes and click the [Save] button to commit these changes to the User record.

Conclusion

In this article, I showed you how to create and manage a User in Azure Active Directory.


Dan Rey on Live Streaming

Comments [0]

Episode 725

Dan Rey on Live Streaming

Dan Rey shares a lot of technical information online via live streaming on LinkedIn, Facebook, YouTube, and other platforms. He discusses the tools and technologies he uses to make this happen.


Michael Bond had a formula when writing about Paddington the Bear. 

The bear begins with good intentions and a plan; things get messed up due to lack of foresight or lack of skill or bad luck; things work out anyway. Bond executed this plan over the course of just a few pages and repeated it seven times, then bundled those seven stories into a single volume.

The plan works because Paddington - the anthropomorphic bear adopted by the Brown family - is charming and likable and readers want to know into what mischief he will find himself next.

"Paddington at Work" sticks to this plan and amuses the reader with another delightful collection of tales.

Paddington returns from his sea trip to South America and is startled by the appearance of the Browns aboard his ship.

Paddington is taken in by a swindler who sells him shares in a fake company.

Paddington attempts to build a serving hatch in a wall, but forgets which wall leads to the outside.

Paddington fills in for a barber without being asked or trained.

Paddington is a last-minute substitute at a ballet.

There is nothing surprising in these stories. They are exactly what fans of the talking bear will expect. 

And they are fun!


Natasha Trethewey and me
I recently met Natasha Trethewey. She spoke at the Printers Row Lit Fest near my home, and she was kind enough to pose for a photo with me after her reading. When I heard she was coming, I knew I had to read some of her work before seeing and hearing her. I began with "Thrall" - a 2012 collection of poems about life and death and race and art.

She opens with an Elegy to her father - a memory of the two of them fishing together when she was a young girl, and her parents were still married.

Some nights, dreaming, I step again into the small boat that carried us out and watch the bank receding— my back to where I know we are headed.

The title is misleading as it was published two years before her father's death, but it captures beautifully her relationship with the man who gave her life.

She returns to her father and her mother multiple times in this collection, mostly remembering them separately, as they divorced when she was six. Despite their brief time together, one must believe that the marriage between a white Canadian and a black Mississippian had a powerful impact on their daughter growing up in Mississippi at a time when interracial marriage was illegal in that state. Another incident that impacted young Natasha was the death of her mother - murdered by her second husband when Natasha was 19 years old.

Trethewey writes of death, but it is not always sad. Trethewey's poems radiate hope and a fondness for the life that once was - fishing with her father; or her mother pulling young Natasha from the water as the nearly-drowned little girl sees "sun, a corona of light around her face".

In between, Trethewey authors poems inspired by art - mostly art depicting blacks and mulattos and whites interacting in colonial settings. Of course, I had to look up the paintings that inspired these poems and shift my gaze between the words on the page and the image on my computer.

"Thrall" was published just as Trethewey was named Poet Laureate of the United States - a position she held until 2014. The emotion in this volume shows why she was granted such an honor.

 


"A Crown of Swords" is Book 7 of Robert Jordan's 14-volume "Wheel of Time" series. The title refers to the crown worn by the king of Ilyan - a crown literally composed of swords, making it difficult to wear physically and metaphorically.

In this book, Rand wonders if he is going insane, but is reassured when he finds a new lover, who can partially predict his future. He spends most of the novel gathering his forces - via conquering, persuasion, and inspiration - in order to invade Ilyan and battle the evil Sammael.

Other star-crossed lovers finally hook up after a long separation; while Elayne and Nynaeve with the help of Mat continue their quest for the Bowl of the Winds, which can halt the unnatural global warming brought on by the dark ones; and Egwayne asserts her power as the new Amyrlin Seat. Oh, and Mat is raped by a queen.

I am now halfway through Robert Jordan's massive "Wheel of Time" series, and I feel committed. Each book which hovers around a thousand pages, but I am convinced Jordan could have told this story in far less space.

I like the characters and I like the story. But I wish Jordan would get on with it.

 


GCast 134:

Managing Microsoft Edge Profiles

The Microsoft Edge web browser allows you to create and manage multiple profiles on the same machine. Using multiple profiles, you can open browser sessions on the same machine that are associated with different users or different accounts. This can be useful to keep separate your work and personal browser settings and your company settings separate from each customer's environment. In this video, you will learn how to create and manage Microsoft Edge profiles.



Overview

In a recent article, I showed you how to errors in a Java Spring Boot application. You can start with the code from that article, which is found here.

This article will describe a more elegant way of handling exceptions.

The Problem with Try/Catch

Here is the relevant error-handling code from the controller's DivideNumbers method, described in the previous article:

@PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
    Integer firstNumber = input.getFirstNumber();
    Integer secondNumber = input.getSecondNumber();
    String personName = input.getPersonName();
    Integer quotient = null;
    try {
        quotient = mathService.DivideNumbers(firstNumber, secondNumber);
    } catch (ArithmeticException e) {
        String message = "An error has occurred, " + personName + ": " + e.getMessage();
        DivideNumbersOutput output = new DivideNumbersOutput(null, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
    } catch (Exception e) {
        String message = "An error has occurred, " + personName + ": " + e.getMessage();
        DivideNumbersOutput output = new DivideNumbersOutput(null, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
    }
    String message = firstNumber + " divided by " + secondNumber 
        + " is " + quotient + ", " + personName;
    DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
    return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
}

There is nothing wrong with this code. It handles errors gracefully. However, it can become inefficient if we have the same error handling in many methods of our application. In that case, we will end up copying and pasting the same code in multiple places, and we will need to maintain the same code in multiple places.

The Spring framework provides a built-in way to address this concern using the @ControllerAdvice annotation, which allows you to handle exceptions across all modules of an application.

We will make a slight change to our application, adding MathInput and MathOutput model classes, These are similar to the input and output classes for adding and dividing numbers, in order to provide generic inputs and outputs for our math methods.

public class MathInput {
    private Integer firstNumber;
    private Integer secondNumber;
    private String personName;

    public Integer getFirstNumber() {
        return firstNumber;
    }
    public void setFirstNumber(Integer firstNumber) {
        this.firstNumber = firstNumber;
    }

    public Integer getSecondNumber() {
        return secondNumber;
    }
    public void setSecondNumber(Integer secondNumber) {
        this.secondNumber = secondNumber;
    }

    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}
public class MathOutput {
    private Integer result;
    private String message;

    public Integer getQuotient() {
        return result;
    }
    public void setQuotient(Integer result) {
        this.result = result;
    }

    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    public MathOutput(Integer result, String message) {
        super();
        this.result = result;
        this.message = message;
    }
}

Next, we add a class to hold a ControllerAdvice class that will contain all the error handling for our application. I named this class DgTestControllerAdvice and decorated it with @ControllerAdvice to indicate it is a ControllerAdvice class.

@ControllerAdvice
public class DgTestControllerAdvice {

I added two methods to this class: One to handle errors of type ArithmeticException and one to handle all other exceptions. Each class is decorated with three annotations:

  • @ExceptionHandler specifies the exception that will trigger calling this method
  • @ResponseStatus specifies the HTTP Response Code to return to the client. This frees us from having to create a ResponseEntity class
  • @ResponseBody tells Spring to serialize the body of the response as JSON

Here are the two methods:

@ExceptionHandler(ArithmeticException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
MathOutput handleArithmeticException(ArithmeticException ex) {
    String message = "Math error: " + ex.getMessage();
    MathOutput mathOutput = new MathOutput(null, message);
    return mathOutput;
}

@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
String handleException(Exception ex) {
    String message = "Error: " + ex.getMessage();
    return message;
}

Of course, we can add more logic to these methods (log the error, for example), but I kept it simple to demonstrate how the pattern works.

Now that we have a @ControllerAdvice class with exception handler methods, we no longer need to clutter our controller with error handling code. The refactored DivideNumbersPost method becomes much simpler and the business logic is more clear without the try/catch structure.

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }

Error Handling Calling DivideNumbers With Controller Advice
Fig. 1 shows the results of running this application and calling the DivideNumbers method using Postman.

Fig. 1

Conclusion

Adding a ControllerAdvice class to your application and moving error handling logic into that class can simplify your code, make error handling reusable and easier to maintain, and improve the readability of your business logic. This article walked you through how to implement this pattern. You can find the code here.


Episode 724

Caito Scherr on Apache Flink

Ververica Developer Advocate Caito Scherr talks about Apache Fink - an open source tool designed to ingest and manage streaming data.

Links:

https://flink.apache.org/ 

https://www.ververica.com/ 

https://www.youtube.com/channel/UCxt7B_fmLplq0OMRVxLiE8w 

 


Todd Rundgren and Adrian BelewWhen we lost David Bowie to liver cancer in 2016, we lost one of the most innovative musicians of his generation.

Friday night at the Copernicus Center in Chicago, a group of musicians tried to recapture and pay tribute to Bowie's innovation and genius.

Todd RundgrenThe "Celebrating David Bowie" tour was organized and hosted by producer/guitarist Angelo "Scrote" Bundini. Scrote organized an all-star cast of musicians, highlighted by Todd Rundgren and Adrian Belew. Among his many accomplishments, Belew worked with Bowie on multiple occasions as a session musician, touring musician, and musical director. They were joined on stage by Spacehog's Royston Langdon and Fishbone's Angelo Moore. Rundgren, Belew, Langdon, and Moore alternated singing lead vocals on 30 David Bowie compositions, while the others backed them up, accompanied by a talented core of Ron Dziubla (saxophone, keyboards), Angeline Saris (bass guitar), and Travis McNabb (drums).

Belew stayed mostly in the background in his white t-shirt and ruby slippers, while the other three vocalists changed costumes between songs and strutted across the stage with extra flamboyance when they sang lead. It was a presentation reminiscent of Bowie himself.

As a special treat, former Bowie backup singer and girlfriend (and Chicago native) Ava Cherry joined the band for a few songs.

Angelo Moore and Ava

The band played many of David's biggest hits, including "Young Americans", "Fame", "Golden Years", and "Space Oddity". Unsurprisingly, they played "Pretty Pink Rose" - a song co-written by Belew and Bowie which the two originally performed as a duet on Belew's "Young Lion's" album. The show reached a climax with energetic back-to-back-to-back renditions of "Let's Dance", "Rebel Rebel" and "All the Young Dudes". Moore stepped off the stage into the audience to dance with crowd members and climb across the top of the chairs, without missing a beat or note in his singing.

For the encore, they pulled a dozen audience members onto the stage and danced with them to "Suffragette City" before closing with art rock anthem "Heroes".

I do not usually enjoy tribute bands, preferring to see the original artists when possible. This was an exception. I was already a fan of Belew and Rundgren, I appreciated the connection many of the performers had with David Bowie, and I recognized the enthusiasm they poured into the evening's performance.


Matt Maeson in concert I was a high school senior in 1979 when I first saw The Who in concert. After wrestling practice, my friends and I drove over 30 miles to the Pontiac Silverdome to watch the show. Half of my friends backed out of the concert because their parents feared for their safety after a tragedy at the Cincinnati concert a week earlier left eleven people dead. Original drummer Keith Moon had died the year before and was replaced by Kenny Jones, but I remember an excellent concert.

For 43 years, that was my only direct experience with this legendary rock group. Until Wednesday night, when I sat in Chicago's United Center and saw The Who for the second time. Bassist John Entwistle has also left us (dead of a heart attack in 2002) but founding members Roger Daltrey and Pete Townshend remain and they were always the heart of the band. Daltrey provided lead vocals on most of their songs and led the band on stage, while Townshend wrote most of their music and played some memorable guitar riffs.

The mod music of The Who helped define the post-Beatles British invasion of the 1960s, but they were much more than that. Their music evolved over time, which allowed them to remain relevant throughout the 1970s and early 1980s.

Wednesday night's Chicago show drew on music from most of their entire career, including some of their biggest hits ("Who Are You?", "Eminence Front", "Won't Get Fooled Again") and deeper album tracks ("Naked Eye", "Ball and Chain"). They also performed abbreviated versions of Townshend's two classic rock operas: "Tommy" and "Quadrophenia". A symphony orchestra consisting of Chicago musicians accompanied them, contributing to the arrangements of the rock operas and some of the other songs. Noticeably missing from the set were any cuts from the pre-Tommy catalog which featured more stripped-down rock and roll. 

The orchestra left the stage in the middle of the show for a 5-song set featuring the core band - Daltrey and Townshend, along with two keyboards, a bass guitar, drums, and a small string section. Pete's brother Simon Townshend and violinist Katie Jacoby stood out among this group. Jacoby was particularly impressive when she came to the front of the stage to dance while playing to close out "Baba O'Reilly", on which the concert ended.

Roger Daltrey and Pete Townshend are in the back half of their 70s, but they still possess the talent and energy for a 2+ hour arena show. They danced and engaged the audience, and each performed their signature stage moves - Daltrey swinging his microphone in a wide arc and Townshend striking his guitar strings with a windmill windup. Daltrey's voice was particularly impressive late in the show when he showed off his range on "Love Reign O'er Me".

If this was the last time I see The Who live, it is a memory I will carry with me for another 43 years.


Overview

Microsoft's Ignite conference kicked off Wednesday morning. It took place in Seattle, but was broadcast live globally, so most of the "attendees" watched it virtually.

As usual, the conference began with a keynote address from Microsoft CEO Satya Nadella.

Satya acknowledged that we are now going through a period of historic economic, societal, and technological change. To address this, he asserted that we must "do more with less!" - a phrase he repeated throughout the keynote and which he described as "applying technology to amplify what you can do and... what an organization can achieve".

He focused on five key "imperatives":

  • Be data-driven and optimize with Azure
  • Deliver efficiency with Automation and AI
  • Innovate with a cloud developer platform
  • Re-energize your workforce with Microsoft 365
  • Protect everything, everyone, everywhere

Be data-driven and optimize with Azure

The cloud allows you to scale your applications and data horizontally and vertically.

Azure Arc allows you to extend applications from Azure to on-premise, edge, and multi-cloud environments.

Deliver efficiency with Automation and AI

Azure provides AI models available for Microsoft AI and OpenAI services

Microsoft Designer is a new tool designed to generate images based on a description.

Innovate with a cloud developer platform

Satya announced improvements to GitHub Co-Pilot:

  • Co-Pilot can generate code based on a description of what that code should do
  • It can explain existing code in plain English
  • It can translate code from one language to another

Enhanced Power Apps features include:

  • Generate flows based on plain text description
  • AI Builder - a tool that integrates AI capabilities into Power Apps by selecting from a set of templates

Re-energize your workforce with Microsoft 365

He announced a number of new features for Microsoft Teams and tools that integrate with Teams, including the following:

  • Teams Premium, which features advanced meeting protection to protect privacy of meetings
  • Teams Intelligent which provides a recap of a Teams meeting
  • New apps (e.g., SAP adaptive cards)
  • Mesh Avatars: animated graphics that represent an attendee without them turning on camera
  • Microsoft Syntex, which provides automatic summarization, classification, and translation of documents
  • Microsoft Places provides tools to match available people and locations, making it easier to schedule meetings at the right time and place in a hybrid work environment.
  • Microsoft Viva brings actionable insights and connections into Teams to drive productive hybrid work. Viva Sales integrates CRM, teams, and Outlook for an integrated view of customer data.

Edge - Microsoft's web browser - also includes some new features, the most interesting of which was Edge Workspace, which allows multiple users to share the same set of tabs

Windows 11 will have enhanced phishing protection built in;

and Windows 365 is a SAAS offering for a Windows desktop that can be accessed from your Start menu or taskbar.

Protect everything, everyone, everywhere

This section was about security and focused on the following products:

  • Entra for identity protection
  • Purview for data governance
  • Priva to protect privacy
  • Intune to protect and manage endpoints

A significant new feature for Microsoft Defender provides security recommendations for multi-cloud deployments.

Conclusion

Satya promised over 100 updates would be announced at Ignite. Some of the features he highlighted are available now, some are in preview, and some will be available in a few months. It was not always clear to me the status of each feature or product when he described it.

The most compelling features for me were the enhancements to GitHub Co-Pilot; but each of you should find something to increase your productivity among this week's announcements.


Overview

In a recent article, I showed you how to create and catch custom errors in a Java Spring Boot application. This article will expand on that and add code to catch built-in exceptions.

You can start with the code from the previous article, which is found here.

Some Error-Prone Code

Let's add some code to our application to perform arithmetic division. We will start with input and output model classes to allow our controller to send and receive JSON.

package com.dgtest.dgtest.models;

public class DivideNumbersInput {
    private Integer firstNumber;
    private Integer secondNumber;
    private String personName;

    public Integer getFirstNumber() {
        return firstNumber;
    }
    public void setFirstNumber(Integer firstNumber) {
        this.firstNumber = firstNumber;
    }

    public Integer getSecondNumber() {
        return secondNumber;
    }
    public void setSecondNumber(Integer secondNumber) {
        this.secondNumber = secondNumber;
    }

    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}
package com.dgtest.dgtest.models;

public class DivideNumbersOutput {
    private Integer quotient;
    private String message;

    public Integer getQuotient() {
        return quotient;
    }
    public void setQuotient(Integer quotient) {
        this.quotient = quotient;
    }

    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    public DivideNumbersOutput(Integer quotient, String message) {
        super();
        this.quotient = quotient;
        this.message = message;
    }
}

Next, we will add a DivideNumbers method to our service, including the interface from which the service inherits (and any other classes that implement that interface)

public interface MathService {
    Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException;
    Integer SubtractNumbers(Integer firstNumber, Integer secondNumber);
    Integer DivideNumbers(Integer firstNumber, Integer secondNumber);
}
@Qualifier("MathServiceImpl")
@Service
public class MathServiceImpl implements MathService {

    ...

    @Override
    public Integer DivideNumbers(Integer firstNumber, Integer secondNumber){
        Integer quotient = firstNumber / secondNumber;
        return quotient;
    }
}

Finally, we add a method to our controller that calls the service and returns the result wrapped in a ResponseEntity object.

@RequestMapping("math")
@RestController
public class MathController {

    ...

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }
}

We can run the application and call it from a tool like Postman by sending an HTTP POST request to the math/DivideNumbers endpoint, as shown in Fig. 1.

DivideNumbers API call with 200 response
Fig. 1

This works fine unless we pass 0 as the second number. In that case, we get a generic exception, as shown in Fig. 2.

DivideNumbers API call with 200 response with Unhandled Exception
Fig. 2

As you can see, the API returns an HTTP 500 (Internal Server Error) that contains very little useful information. We can improve this API by returning something more useful to the client.

Catching Built-In Exceptions

We can handle these exceptions more gracefully by adding a try/catch structure around the code in our controller, as shown below:

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = null;
        try {
            quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        } catch (ArithmeticException e) {
            String message = "An error has occurred, " + personName + ": " + e.getMessage();
            DivideNumbersOutput output = new DivideNumbersOutput(null, message);
            return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
        } catch (Exception e) {
            String message = "An error has occurred, " + personName + ": " + e.getMessage();
            DivideNumbersOutput output = new DivideNumbersOutput(null, message);
            return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
        }
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }

We have moved the call to the MathService method inside a TRY block and added two CATCH blocks. The first CATCH block will trap an Arithmetic error, which is what happens we try to divide by 0. The second CATCH block traps any other errors that occur.

With this code change, the results are more useful when a client passes a 0 as a denominator, as shown in Fig. 3.

DivideNumbers API call with 200 response with Exception Handling
Fig. 3

Conclusion

This article shows an example of gracefully handling errors in a Java Spring Boot REST API. You can find the code here.


Overview

In a recent article, I showed you how to add some basic error handling to your Spring Boot application. The approach I showed will work, but it tends to clutter up your controller with error handling, which is not business logic. We can achieve better separation of concerns (and, therefore, more readable and maintainable code), if we move our error handling code outside the controller.

You can start with the code from the previous article, which is found here.

Custom Exceptions

Let's create a custom exception to specifically handle missing arguments. This exception will inherit from Java's Exception class, as shown below.

public class MissingArgumentsException extends Exception {
    public MissingArgumentsException(String errorMessage) {
        super(errorMessage);
    }
    public MissingArgumentsException(String errorMessage, Throwable err) {
        super(errorMessage, err);
    }
}

Next, we will add code in our service's AddNumbers method to check for missing arguments and throw the custom exception, if an argument is missing.

@Qualifier("MathServiceImpl")
@Service
public class MathServiceImpl implements MathService {

    ...

    @Override
    public Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException {
        logger.info("MathServiceImpl.AddNumbers() called with " + firstNumber + " and " + secondNumber);
        if (firstNumber==null || secondNumber==null) {
            String message = "Missing input";
            throw new MissingArgumentsException(message);
        }
        Integer sum = firstNumber + secondNumber;
        return sum;
    }

    ...
}

Java has a rule that you must add a throws clause to a method declaration before we can throw an exception within the method. Hence, in order to add the line:

throw new MissingArgumentsException(message);

we must change the method declaration to:

public Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException {

Because our class inherits from an interface (MathService), we must also modify the method declaration within the interface and within any other classes that implement that interface.

public interface MathService {
    Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException;
    ...
}

Finally, we modify our controller method, wrapping the call to the service in a try/catch structure and returning an error HTTP code and error message only if the exception is caught in the catch block, as shown below:

@PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
public ResponseEntity<AddNumbersOutput> AddNumbersPost(@RequestBody AddNumbersInput input) throws MissingArgumentsException {
    Integer firstNumber = input.getFirstNumber();
    Integer secondNumber = input.getSecondNumber();
    String personName = input.getPersonName();

    Integer sum = null;
    try {
        sum = mathService.AddNumbers(firstNumber, secondNumber);
    } catch (Exception e) {
        String message = e.getMessage();
        AddNumbersOutput output = new AddNumbersOutput(null, message);
        return new ResponseEntity<AddNumbersOutput>(output, HttpStatus.BAD_REQUEST);
    }

    ...

    String message = "The sum of " + firstNumber + " and " + secondNumber 
        + " is " + sum + ",  " + personName;
    AddNumbersOutput output = new AddNumbersOutput(sum, message);
    return new ResponseEntity<AddNumbersOutput>(output, HttpStatus.OK);
}

Now, if we call the Controller method, using a tool like Postman, and try to omit one of the required arguments, the controller will return an HTTP 400 (BAD REQUEST) and an error message in the body, as shown in Fig. 1

Bad HTTP request and response - missing input data

Conclusion

The behavior does not change much from our implementation in the earlier article, but structuring our code like this makes it cleaner by keeping error handling out of the controller. This would be more apparent if we had a controller that called a series of methods, some of which were nested multiple levels deep. We could throw exceptions in the called methods and only catch them in the controller, which sits at the top of the stack. Exceptions would bubble up and be handled in one place.

You can find the code for this article here.


Overview

This is another in a series of articles I am writing about creating application using Java Spring Boot. The most recent article can be found here and the code for that article can be found here.

The web service we created in previous articles always returned an HTTP 200 response, indicating success. But that will not always be the case. Sometimes, things can go wrong; for example, a user can enter bad input. In cases where everything is not OK, you will want to return a different HTTP response. HTTP response codes in over 400 indicate a problem.

In this article, I will show how to check for invalid input and return a BAD REQUEST response (HTTP 400).

Input, Output, and a Controller method

Let's start with a Controller method that adds two numbers together and two model classes: One that defines the input of the controller method and the other that defines the output. Input and output will both be passed as JSON, but we can define a Java method with the same structure as the JSON.

Here is a sample JSON input that includes two numbers (firstNumber and secondNumber) and the name of the person making the request:

{
    "firstNumber": 10,
    "secondNumber": 20,
    "personName": "David"
}

and here is a Java class that describes the JSON object:

public class AddNumbersInput {
    private Integer firstNumber;
    private Integer secondNumber;
    private String personName;

    public Integer getFirstNumber() {
        return firstNumber;
    }
    public void setFirstNumber(Integer firstNumber) {
        this.firstNumber = firstNumber;
    }

    public Integer getSecondNumber() {
        return secondNumber;
    }
    public void setSecondNumber(Integer secondNumber) {
        this.secondNumber = secondNumber;
    }

    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}

When we add together the two numbers, we will return JSON containing the sum and a friendly message to the caller, similar to the following:

{
    "sum": 30,
    "message": "The sum of 10 and 20 is 30,  David"
}

Here is a Java model class with the same structure:

public class AddNumbersOutput {
    private Integer sum;
    private String message;

public Integer getSum() {
        return sum;
    }
    public void setSum(Integer sum) {
        this.sum = sum;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public AddNumbersOutput(Integer sum, String message) {
        super();
        this.sum = sum;
        this.message = message;
    }
}

Next, we create a controller class, to accept the input, add the numbers together, and return the output in JSON format:

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<AddNumbersOutput> AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity<AddNumbersOutput>(output, HttpStatus.OK);
    }

A client can send an HTTP POST to the AddNumbers endpoint and will receive an HTTP 200 (OK) response with the expected JSON in the response body. Fig. 1 shows this response when we call the web service using Postman.

Good HTTP request and response
Fig. 1

But, if the client omits any of the required properties in the input JSON, we will not be able to generate a response. We can check for this early in the Controller method and construct an appropriate message and return a BAD REQUEST (400) HTTP response in this case.

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        if (firstNumber==null || secondNumber==null || personName==null) {
            String message = "Missing input";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity(output, HttpStatus.OK);
    }

Bad HTTP request and response - missing input data
Fig. 2 shows this scenario.

Fig. 2

We may add our own business logic to determine what defines an error. We may, for example, decide that the name "Bob" is not a valid name and want to return an error if someone tries to pass "Bob" as the personName, as shown below:

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        if (firstNumber==null || secondNumber==null || personName==null) {
            String message = "Missing input";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }
        if (personName.compareTo("Bob")==0) {
            String message = "Bob is not welcome here";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity(output, HttpStatus.OK);
    }

Fig. 3 shows this request and the resulting response.

Bad HTTP request and response - invalid personName Bob
Fig. 3

Conclusion

This is a contrived example, but we can add our own logic and return what we feel is appropriate. The main point is that if we have a problem (for example, bad inputs), we should return to the client an HTTP error code and we should return some useful information without revealing any sensitive data.

You can find the code for this article here

 


Episode 723

Simeon Kakpovi and Greg Schloemer on a Cybersecurity game

Greg Schloemer and Simeon Kakpovi discuss KP7 - an open source game they created to teach cybersecurity to students, using a database of simulated company data which includes simulated threats. They brought this game to two colleges to and showed it to students, allowing them the opportunity to learn about and deal with cybersecurity issues.

Links:

https://github.com/kkneomis/kc7
https://dataexplorer.azure.com


Matt Maeson in concertMatt Maeson is the best musician I never heard of. My friend Christina introduced me to his music, which she loved enough to fly from Las Vegas to Chicago to see him perform for two nights at the House of Blues. I joined her for the Friday evening performance, and I was more than impressed.

Maeson has a loyal following, as indicated by his success on the alternative music charts and by the number of fans who packed the venue and sang along to his songs.

Many of his songs lend themselves to group singing with their anthem-like quality, such as "Blood Runs Red", "Cut Deep", and "Cringe". But even his slower tunes like "Hallucinogenics" and "Beggars Song" (his encore) inspired audience participation.

Matt was raised by missionaries who used music to connect with their audience. You can hear influences from rock, Christian music, and gospel in his music. He brings the passion of an evangelist to his live performance.

I am happy he has moved from the unknown to the known in my mind. Thank you, Christina.


Hay Fever set It is not a crime to overact. Sometimes, it is a good thing!

Betsy Pennington Taylor proved that Saturday night at the City Lit Theater as she portrayed aging stage actress Judith Bliss in a production of Noel Coward's "Hay Fever". 

Taylor's performance was deliberately over the top as Bliss's character was a melodramatic retired starlet, who served as the matriarch of an exceedingly dysfunctional family. 

The four Blisses (mother, Stephen Fedo as father David, Travis Shanahan as son Simon, and Lizzie Williams as daughter Sorel) have each invited a guest to spend a weekend at their summer home, without informing any of the others. Not only is the house too crowded, but the guests quickly experience discomfort at the bizarre attitudes of the Bliss family. They are self-absorbed and rude and obnoxious; but they are clever and funny. The houseguests' pain becomes the audience's pleasure. 

Hay Fever castThe guests themselves are not particularly likable - they range from dimwitted to manipulative - but their flaws pale beside the antics of their bohemian hosts. Gerrit Wilford's performance was particularly memorable as he responds in shock when Judith overreacts to his flirtations, announcing that she must now inform her husband. The entire family tends to overreact in every situation. It is this behavior that repulses outsiders yet bonds the family together. And it is what makes "Hay Fever" a success.


Sergio Mendes in concert After dozens of visits to Chicago's City Winery, this was the first time I remember the entrance line snaking through the lobby and out the front door onto the sidewalk. The crowd came to see Sergio Mendes and the theater was full. I was fortunate to buy the last available seat - a stool at the bar near the back - and I was fortunate to see an excellent performance!

Mendes opened with the bossa nova music that made him a global sensation in the 1960s - a style of jazz exported from his native Brazil. He continued with some sambas and a set of songs featuring African-based rhythms of Brazil, before moving into some Rhythm and Blues songs. 

Near the end of the show, the band performed some of his R&B hits from the 1960s, 1970s, and 1980s, including "Going Out of My Head", "The Look of Love", "Fool on the Hill", and his biggest hit "Never Gonna Let You Go"!

Notably absent from the evening's playlist was the hip hop music into which he has been delving lately.

Mendes was able to get the crowd to its feet by playing the opening chord of "Mas Que Nada" - his first international hit from 1965! The entire audience rose and danced in rhythm to the music.

In addition to Mendes's keyboards, the band consisted of guitar, bass, drums, percussion, and Scott Mayo, who did everything. Mayo was impressive on flute, piano, keyboards, and vocals.

Two singers performed with the group: American Katie Hampton and Mendes's wife Gracinha Leporace. Hampton's voice resonated with the spirit of the music.

Most of the lyrics were in Portuguese, which mattered not at all to those of us who do not understand that language. Music is an international language that cuts across cultures and Sergio Mendes proved that on this night.

Photos


GCast 133:

Configuring an Azure App Service Identity Provider

Learn how to configure Azure Active Directory as an Identity Provider for an Azure Web Application. An Application Registration points to the App Service containing the web app.


In a previous article, I showed you how to automatically add information when logging from a Java Spring application to Application Insights logs using the Mapped Diagnostic Context. The code for that article is here.

The example I used in that article generated a Request Key every time a controller method is calling. An alternative solution is to have the client that calls the controller pass the Request Key to method in the HTTP Header.

Request Headers

To read the Header of an HTTP request in a controller method, add a parameter of type HttpServletRequest.

An HTTP header can contain one or more key/value pairs. The HttpServletRequest object allows you to read the value of a given key, using the getHeader method and pasing in the key. If we add an HTTP Header with the key "X-Request-Key" to our request, we can retrieve it java with a line like:

String request requsestKey = request.getHeader("X-Request-Key");

The modified controller code looks like this:

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber,
    HttpServletRequest request) {
        String requestKey = request.getHeader("X-Request-Key");
        if (requestKey == null) {
            requestKey = UUID.randomUUID().toString();
        }
        MDC.put("Request-Key", requestKey);
        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        return new ResponseEntity<Integer>(sum, HttpStatus.OK);
}

Using a tool like Postman, I can send an HTTP request and add a custom header to that request, as shown in Fig. 1.

Custom HTTP Request Header in Postman
Fig. 1

This works the same as the code in my previous article, except the calling client is given the opportunity to pass in the Request Key, rather than having the controller generate it.

Response Headers

An HTTP Response also contains Header information and we can write Java code to include custom key/value pairs in the Response Header.

The HTTPHeaders object (in the org.springframework.http namespace) allows you to write to the response header. This object has a set method that accepts a key and a value as parameters, so you can pass that back with the HTTP Response Headers.

Our existing code returned a ResponseEntity object, which has a constructor overload that allows us to include an HTTPHeaders object in the response.

Here is the updated code in the controller:

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber,
    HttpServletRequest request) {
        String requestKey = request.getHeader("X-Request-Key");
        if (requestKey == null) {
            requestKey = UUID.randomUUID().toString();
        }
        MDC.put("Request-Key", requestKey);
        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);

        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.set("X-Request-Key", requestKey);
        return new ResponseEntity<Integer>(sum, responseHeaders, HttpStatus.OK);
}

Now, when we run the application and call this controller method, we not only get information in the body of the HTTP Response; we also receive the Request Key in the HTTP Response Header, as shown in Fig. 2.

Custom HTTP Response Header in Postman
Fig. 2

Conclusion

In this article, you learned how to read Header information from an HTTP Request and to write Header information to an HTTP Response, using Java code.

You can find the code for this article here.


In a previous article, I showed you how to log to Azure Application Insights from a Java Spring Boot Application.

When logging a lot of data for a popular application, it can be difficult to search for data related to a specific call to a method. One way to address this is to generate a unique key for each call and record that key with every entry logged by the method or by any method called by that method.

You could pass this key as a parameter to every method, and include it with each logging statement; but that is inefficient and there is a good chance you may sometimes forget to pass it. The Mapped Diagnostic Context (MDC) service provides a way for you to include one or more key-value pairs in every Application Insights logging entry made by the current method and by any methods called in-process by that method.

Imagine I have a controller class that calls a service class and I have instantiated a logger object at the top of each:

private Logger logger = LoggerFactory.getLogger(MathController.class);

and that I have a Controller method named "Add" that adds together two number and I do some logging in that method, as shown below

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber) {
        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);
        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        return new ResponseEntity<Integer>(sum, HttpStatus.OK);
}

and that this method calls a service to do the work of adding together the numbers and that service also does some logging, like so:

@Override
public Integer AddNumbers(Integer firstNumber, Integer secondNumber) {
    logger.info("MathServiceImpl.AddNumbers() called with " + firstNumber + " and " + secondNumber);
    Integer sum = firstNumber + secondNumber;
    return sum;
}

I can add the following two lines to the top of my Controller's Add() method:

String requestKey = UUID.randomUUID().toString();
MDC.put("Request-Key", requestKey);

This generates a new random key on each call to the controller and stores that key in the MDC object.

The modified method now looks like this:

@GetMapping("add/{firstNumber}/{secondNumber}")
public ResponseEntity<Integer> Add(
    @PathVariable("firstNumber") Integer firstNumber, 
    @PathVariable("secondNumber") Integer secondNumber) {
        String requestKey = UUID.randomUUID().toString();
        MDC.put("Request-Key", requestKey);

        logger.info("MathController.Add() called with " + firstNumber + " and " + secondNumber);

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        return new ResponseEntity<Integer>(sum, HttpStatus.OK);
}

Now, every time I log from this method or any in-process calls that this method makes, it will automatically add the Request-Key name-value pair to the log entry in Application Insights. I do not need to add the MDC.put() code to the service method. Because it is called in-process, it already knows to include the extra logging information.

When I run this code and call the controller method, Application Insights will include an extra custom property, as shown in Fig. 1.

Application Insights Trace log
Fig. 1

This is a simple, low-code way to add extra information to your logging, making it easier to track and troubleshoot your application.

You can find the code for this article here.


Episode 722

Mike Benkovich on Infrastructure as Code

Mike Benkovich describes the value of Infrastructure as Code, allowing us to deploy assets in a repeatable fashion. He also walks through some examples using ARM templates, BICEP, and Terraform.

https://github.com/BenkoTIPS/ptc


September 2022 Gratitudes

Comments [0]

9/5

Today I am grateful for my first visit to Vail, CO.

 

9/6

Today I am grateful to attend a baseball game at Coors Field for the first time.

 

9/7

Today I am grateful for

- a visit to the Denver Museum of Art yesterday

- a long weekend in Colorado with friends

 

9/8

Today I am grateful for 5 days in Colorado

 

9/9

Today I am grateful for an invitation to use the DALL-E AI tool.

 

9/10

Today I am grateful for front row tickets to an outstanding Al Dimeola concert last night

 

9/11

Today I am grateful to hear and meet former United States Poet Laureate Natasha Trethewey yesterday.

 

9/12

Today I am grateful for the return of NFL football

 

9/13

Today I am grateful to my trainer, who always spends a few extra minutes with me than she is required each session.

 

9/14

Today I am grateful for a frictionless return of a defective product.

 

9/15

Today I am grateful GrubHub finally reversed a charge that someone else made to my account.

 

9/16

Today I am grateful to sit an outdoor café last night, sipping a pilsner and reading poetry.

 

9/17

Today I am grateful to witness enthusiastic celebrations of Mexican Independence Day around the city this week

 

9/18

Today I am grateful for my 300th book review on GoodReads.com

 

9/19

Today I am grateful for an afternoon listening to live music on the patio at Fitzgerald's

 

9/20

Today I am grateful to see Roxy Music in concert last night

 

9/21

Today I am grateful for a new pair of eyeglasses.

 

9/22

Today I am grateful to see Michelle Branch in concert last night.

 

9/23

Today I am grateful for lunch with Jeffrey yesterday

 

9/24

Today I am grateful I was able to complete a bunch of half-written blog posts yesterday.

 

9/25

Today I am grateful to hang out in downtown Minneapolis last night with Tim

 

9/26

Today I am grateful to see an exciting Vikings-Lions game on my first visit to U.S. Bank Stadium with my sons.

 

9/27

Today I am grateful for dirty vodka martinis with extra garlic-stuffed olives

 

9/28

Today I am grateful to discover a path along the Chicago River North Branch that I had never traveled before last night.

 

9/29

Today I am grateful for a beautiful sky last night

 

9/30

Today I am grateful for:

- Live music at the Montrose Saloon last night

- A community get-together at the Aon Center yesterday afternoon

 

10/1

Today I am grateful for lunch yesterday with Dan and Stephen

 

10/2

Today I am grateful to see Noel Coward's "Hay Fever" last night.

 


In his third book about Doctor Dolittle, Hugh Lofting returns his hero to west Africa, where he helps a peaceful African tribe. 

There are multiple stories in "Doctor Dolittle's Post Office". The primary one is featured in the title and tells of how John Dolittle establishes a post office in a small African village - first, to help the residents send messages to one another; but soon expands to send messages to and from other countries using migratory birds to carry them. He even allows animals to send messages across disparate continents. 

In addition to the titular story, we hear of the Doctor helping to capture a slave ship, fending off attacks on the peaceful village from other tribes, and his visit to an ancient turtle living in an isolated region of Africa, who remembers the time of Noah's Biblical flood.

As an intermission, we even get a few short stories told by Dolittle's animal friends.

Doctor Dolittle is a physician turned veterinarian, who learned the language of many of Earth's animals and spent his life helping people and beasts. This is another entertaining book by Lofting that focuses on the Doctor's kindness and the respect that it brings him.

 


GCast 132:

Managing Azure Key Vault Secrets from a .NET App [GCast 132 ]

The Azure.Security.KeyVault.Secrets NuGet package allows you to manage Azure Key Vault secrets in a .NET application. But you must first allow your application to access and manage Key Vault secrets. This video walks you through the whole process.


Overview

In a previous article, I showed how to implement Logging in a Java Spring Boot Application. In this video, I will show how to log to Azure Application Insights. Application Insights is a scalable Azure data store and service that is ideal for storing monitoring information of all kinds.

NOTE: The code for the previous article can be found here. If you did not work through the samples in that article, you can use this code as a starting point for this article.

Azure Application Insights

The first step is to create an Application Insights service.

This article walks you through the steps of creating an Application Insights service.

After Azure creates the service, navigate to its "Overview" blade, as shown in Fig. 1.

Application Insights Overview Blade
Fig. 1

On the "Overview" blade, copy the "Instrumentation Key" (Fig. 2). You will need this later.

Instrumentation Key
Fig. 2

Add Dependencies

To access Application Insights, you will need to register dependencies in the pom.xml file. Open this file and add the following inside the element.


    com.microsoft.azure
    applicationinsights-spring-boot-starter
    2.6.4


    com.microsoft.azure
    applicationinsights-logging-logback
    [2.0,)

Add App Insights Logging Configuration

Next, you need to add additional logging configuration in the logback-spring.xml file. Open this file and add the following inside the element:



The section above defines how to log to Application Insights. To tell your application to use this when logging, we must add an element in our logging section, as shown below:


    
    

When finished, the entire logback-spring.xml file should look like the following:



    
    
    
        
            
                %black(%d{ISO8601}) %highlight(%-5level) %cyan(%logger{36}) - %msg%n
            
        
    
    
        
        
    

Set App Insights Key in Application Properties

Finally, we need to provide the Instrumentation Key to our logging library. A simple way to do this is to add a file named "application.properties" anywhere in our application path (I like to save this in the "resources" folder).

Add the following line to this file:

azure.application-insights.instrumentation-key=aaaaaaaaaaaaaaaaaaaa

where aaaaaaaaaaaaaaaaaaaa is the Application Insights Key you copied above.

Testing It

We can run this code locally and call the greetPerson method with the URL: http://localhost:8080/greetings/greet/David

Logging to Application Insights is not instantaneous. App Insights logging scales very well because it is done asynchronously. But, if you wait a few minutes, you will be able to view the logged information in Application Insights.

A few minutes after running, we should see log statements in Application Insights. To see these log statements, navigate the

Note that, as described in the earlier article, we will only output log statements at or above the level set in logback-spring.xml.

Conclusion

In this article, I showed you how to log to Azure Application Insights in an Azure Spring Boot application. You can find the sample code here.


Overview

Logging is an important part of an application - particularly when trying to troubleshoot problems that occurred in the past.

In this article, we will discuss adding logging to a Spring Boot Java application. Spring contains the Logback logging framework, which we will use in our examples.

In previous articles, I showed how to create a Java Spring Boot application. We will continue working on that application in this article.

NOTE: The code for the previous article can be found here. If you did not work through the samples in that article, you can use this code as a starting point for this article.

Logging Levels

Logging supports five levels of logging. In order, they are:

  1. TRACE
  2. DEBUG
  3. INFO
  4. WARN
  5. ERROR

When you configure logging, you can specify a level of logging and your application will log only messages at that specified level and above. So, if you configure logging at the TRACE level, all five levels are logged; but, if you configure logging at the INFO level, only INFO, WARN, and ERROR messages are logged.

Logging Configuration

Add the file "logback-spring.xml" to any folder in your application path. I like to add it into the "resources" folder.

The following XML configures your application to implement a Console logger and tells Java how to format text in the console. Paste it into the logback-spring.xml file.



    
        
            
                %black(%d{ISO8601}) %highlight(%-5level) %cyan(%logger{36}) - %msg%n
            
        
    

    
        
    

The section tells what to log and where. We set level="INFO", telling our logger to log only those statements at the INFO level and above. This includes INFO, WARN, and ERROR logging statements. It will ignore any TRACE or DEBUG logging statements.

Within the element is an element. The ref attribute tells the application to use a logging appender named "Console". We can find the configuration for this appender near the top of the XML file. Inside the element is information on how to log to the console. The pattern tells what information to log and how to format it. In this case, we want the date, level, and log message. The pattern also includes formatting information, such as color and date formatting. This can be confusing, but you can find references on formatting here.

Code

Now that we have configured our application for logging, we can add some logging code.

In one of the controllers (I chose _GreetingController), create a logger object as a private variable, as shown below:

private Logger logger = LoggerFactory.getLogger(MathController.class);

NOTE: Validate that the Logger and the LoggerFactory classes are in the import org.slf4j libraries

The Logger class contains five methods that write to a log - each at a different level (trace, debug, info, warn, and error).

Add some logging statements to your code. These statements will work in the greetPerson method:

logger.trace("Hello" + personName);
logger.debug(personName +" is Tracy's mom!");
logger.info("Information, please, " + personName);
logger.warn("Warning! Warning! Warning! Dr, Smith, call " + personName);
logger.error(personName + " is exceptional!");

Running the Code

We can run this code locally and call the greetPerson method with the URL: http://localhost:8080/greetings/greet/David

After running, we should see log statements in the console, as shown in Fig. 1.

Log Output
Fig. 1

Note that we have five log statements, but only output three of them. This is because we configured the application to only log level INFO and above.

Conclusion

In this article, I showed you how to implement logging in an Azure Spring Boot application. You can find the sample code here.


Episode 721

Brian Gorman on Messaging, Queueing, and Eventing in Azure

Brian Gorman describes the value of using queueing to build asynchronous applications and the native Messaging, Queueing, and Eventing services in Azure. He discusses the value of each service and when it is appropriate to use each one.

https://github.com/blgorman/ServerlessMessagingDemystified


Michelle BranchIt is difficult to categorize the music of Michelle Branch. She is at times a rock singer, a country singer, a folk singer, and a pop singer. 

Wednesday night at Park West, she was all these things.

For nearly two hours, Ms. Branch played before a packed house of devoted fans, who cheered and sang along to her songs.

Branch's fame peaked in the first half of the 2000's decade when she released back-to-back-to-back hit albums and a string of hit singles. She raked in a string of awards during this period, including a Grammy for "The Game of Love" - a collaboration with Carlos Santana.

While her airplay may have waned in the past fifteen years, her enthusiasm has not. She played all the hits from those years and made them sound fresh. And she mixed in some recent music (she released a new album - "The Trouble with Fever" - earlier this year. Everything was delivered with the energy one would expect from someone who enjoys performing.

Park West has great acoustics for music but spoken words do not transmit well there; so, I did not know what she was saying between songs; but those who heard seemed to greatly appreciate it.

It was an evening in which the crowd drew energy from the performer and the performer drew energy from the crowd.

It was a great evening!

 


Roxy MusicRoxy Music never received the recognition they deserved. In the 1970s and 1980s, they were leaders in the progressive rock music movement. Bryan Ferry's smooth crooner's voice was backed by outstanding musicians like Phil Manzanera (lead guitar), Andy MacKay (saxophones), and Paul Thompson (drums). Roxy's singles never made much noise on the US charts, but they received plenty of airplay on the album-oriented-rock stations of the time and their concerts were always a big draw.

The group disbanded in 1983, following the release of their excellent final album "Avalon". They reformed and toured in the early 2000s then broke up again. It has been over a decade since they toured and almost 40 years since they performed in Chicago.

They returned to the Windy City Monday night at the United Center. 

It should have been a big draw. It was the band's 50th anniversary, their first tour in years, and the core of the original group (Ferry, Manzanera, MacKay, and Thompson) participated. But the concert failed to sell out, so the venue closed off the upper deck, moved the stage forward two sections, and reshuffled tickets to make the arena seem fuller. 

The illusion of fullness did not work, but that did not matter to those in attendance. Roxy Music put on a polished performance, playing for nearly two hours. They selected works from their early artsy days ("In Every Dream Home a Heartache") to their later pop-influenced songs ("More Than This", "Dance Away").

They chose to omit some of their more popular songs (Roxy had a dozen Top 10 hits in the UK), filling the set with many deep cuts from their eight studio albums instead. They still sounded great. Ferry's velvety voice is still strong, the core members still have great technical prowess, and the musicians they hired to fill out the arrangements were top-notch.

This was a concert I had to attend. I grew up listening to Roxy Music. I had never seen them live before, and there is a good chance this is their final tour before they separate for good. 

I made the right choice coming to the show.


Overview

In a previous article, I showed how to refactor a controller and move the business logic into its own service class. We based that service class on an interface.

NOTE: The code for the previous article can be found here. If you did not work through the samples in that article, you can use this code as a starting point for this article.

When we called that service, we explicitly instantiated an instance of the class we created. That works, but it hard-codes a dependency to a specific implementation, which limits our flexibility.

In this article, I will show you how to use Dependency Injection to remove that hard-coded dependency. Spring explicitly supports Dependency Injection.

Declaring a class as a Service

The MathServiceImpl class contains our business logic. To use Spring's Dependency Injection, we must explicitly declare it as a Service. To do this, we decorate the class with the @Service attribute. If we have multiple implementations of the same interface, we distinguish them by also decorating the class with the @Qualifier attribute and providing a unique string to identify this implementation. I typically use the class name as this string, but you may use whatever conventions you like to enforce uniqueness.

An example is shown in the listing below:

@Qualifier("MathServiceImpl")
@Service
public class MathServiceImpl implements MathService {
    @Override
    public Integer AddNumbers(Integer firstNumber, Integer secondNumber) {
        Integer sum = firstNumber + secondNumber;
        return sum;
    }

    @Override
    public Integer SubtractNumbers(Integer firstNumber, Integer secondNumber) {
        Integer difference = firstNumber - secondNumber;
        return difference;
    }
}

We can create a second Service class that implements this same interface and provide a different Qualifier, as shown below:

@Qualifier("MathServiceMockImpl")
@Service
public class MathServiceMockImpl implements MathService {
    @Override
    public Integer AddNumbers(Integer firstNumber, Integer secondNumber) {
        return 10;
    }

    @Override
    public Integer SubtractNumbers(Integer firstNumber, Integer secondNumber) {
        return 5;
    }
}

In this example, we heard coded the return values of the add and subtract methods to 10 and 5, respectively.

This is a common practice if we want to use one class for production and a different class for testing. The production class may contain dependencies to other services, databases, and files; while the testing class may hard code return values to avoid hitting those dependencies.

Instantiating the service class

Now that we have declared the MathService classes as services, we no longer need to explicitly instantiate them: we can now use Dependency Injection to automatically instantiate a class whenever it is used.

In the MathController class, delete the two lines that instantiate MathServiceImpl:

MathService mathService = new MathServiceImpl();

Instead, at the top of the class, add a private variable named "mathService" of type MathService and decorate this with the @Autowired attribute and the @Qualifier attribute. Pass "MathServiceImpl" as the argument to @Qualifier, as shown below:

@Autowired
@Qualifier("MathServiceImpl")
private MathService mathService;

The @Autowired attribute tells Spring to use Dependency injection to create an instance of an object whenever the mathService variable is used.

The @Qualifier attribute tells Spring which class to instantiate.

Here is the new full listing of the MathController class:

@RequestMapping("math")
@RestController
public class MathController {

    @Autowired
    @Qualifier("MathServiceImpl")
    private MathService mathService;

    @GetMapping("add/{firstNumber}/{secondNumber}")
    public ResponseEntity Add(
        @PathVariable("firstNumber") Integer firstNumber, 
        @PathVariable("secondNumber") Integer secondNumber) {
            Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
            return new ResponseEntity(sum, HttpStatus.OK);
    }

    @GetMapping("subtract/{firstNumber}/{secondNumber}")
    public ResponseEntity subtract(
        @PathVariable("firstNumber") Integer firstNumber, 
        @PathVariable("secondNumber") Integer secondNumber) {
            Integer difference = mathService.SubtractNumbers(firstNumber, secondNumber);
            return new ResponseEntity(difference, HttpStatus.OK);
    }
}

When we run this and call the add method, we get the same results as in the last article, but we have fewer hard-coded dependencies.

AddNumbers
Fig. 1

Using a Different Qualifier

We can tell Spring to use a different implementation of MathService by changing the @Qualifier argument.

Replace the @Qualifier line in MathController with the "MathServiceMockImpl", as shown below:

@Autowired
@Qualifier("MathServiceImpl")
private MathService mathService;

Now, when we run this and call the add method, it always returns 10, regardless which parameters we pass, as shown in Fig. 2.

AddNumbers Mock Implementation
Fig. 2

Conclusion

In this article, I showed you how to use the built-in Dependency Injection features of Spring Boot to avoid hard-coded dependencies in your code.

You can find the sample code here.


Overview

In a previous article, I showed how to create a REST API in a Spring Boot application. In the demo we built, all the logic was in the Controller. For a complex application, this can violate the Single Responsibility principal, as the controller method is responsible for both returning data in an appropriate format and performing any business logic to retrieve and/or calculate that data, along with performing required workflows. To achieve a better separation of concerns, we should move any business logic out of the controller and into its own service layer. This will not only simplify our code, but it will allow us to deploy and scale the controller and the business logic separately.

NOTE: The code for the previous article can be found here. If you did not work through the samples in that article, you can use this code as a starting point for this article.

Some Business Logic

For this demonstration, I will create a new controller named "MathController" that contains two methods: add and subtract. These methods each accept two parameters and do exactly what their names suggest: find the sum and the difference between those numbers. The code is shown below:

@RequestMapping("math")
@RestController
public class MathController {
    @GetMapping("add/{firstNumber}/{secondNumber}")
    public ResponseEntity Add(
        @PathVariable("firstNumber") Integer firstNumber, 
        @PathVariable("secondNumber") Integer secondNumber) {
            Integer sum = firstNumber + secondNumber;
            return new ResponseEntity(sum, HttpStatus.OK);
    }

    @GetMapping("subtract/{firstNumber}/{secondNumber}")
    public ResponseEntity subtract(
        @PathVariable("firstNumber") Integer firstNumber, 
        @PathVariable("secondNumber") Integer secondNumber) {
            Integer difference = firstNumber - secondNumber;
            return new ResponseEntity(difference, HttpStatus.OK);
    }
}

If we run this locally and call it from a browser, the output is shown in Fig. 1.

AddNumbers
Fig. 1

There is nothing wrong with this code and it is probably acceptable for simple methods like these. But imagine if we were doing much more than performing simple arithmetic. Imagine complex workflows with dependencies on databases, file systems, or web services. Logic like that does not belong in the controller. It should be split into its own service.

We will pretend the logic above is complex and justifies splitting it into a distinct service.

Interfaces

In many cases, it is a good idea to create an interface on which to base your service classes. This supports polymorphism in your app and allows you to use different implementations for different purposes (for production vs for testing, for example).

I will save all my services and interface in a source folder named "services".

An interface defines the public properties and methods of any class that implements it.

We use the interface keyword to define an interface and list the name, input arguments, and return data type of each public method, as shown below:

public interface MathService {
    Integer AddNumbers(Integer firstNumber, Integer secondNumber);
    Integer SubtractNumbers(Integer firstNumber, Integer secondNumber);
}

Implementation

Now, we can create a class based on this interface. The implements keyword tells Java on which interface a class is based. If we base a class on an interface, we must implement every method in that interface. The code below shows this.

public class MathServiceImpl implements MathService {

    @Override
    public Integer AddNumbers(Integer firstNumber, Integer secondNumber) {
        Integer sum = firstNumber + secondNumber;
        return sum;
    }

    @Override
    public Integer SubtractNumbers(Integer firstNumber, Integer secondNumber) {
        Integer difference = firstNumber - secondNumber;
        return difference;
    }
}

The @Override attribute tells Java that this the methods override an interface.

Changing the Controller

Now, we can modify our controller to call this service, as shown below:

@RequestMapping("math")
@RestController
public class MathController {
    @GetMapping("add/{firstNumber}/{secondNumber}")
    public ResponseEntity Add(
        @PathVariable("firstNumber") Integer firstNumber, 
        @PathVariable("secondNumber") Integer secondNumber) {
            MathService = new MathServiceImpl();
            Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
            return new ResponseEntity(sum, HttpStatus.OK);
    }

    @GetMapping("subtract/{firstNumber}/{secondNumber}")
    public ResponseEntity subtract(
        @PathVariable("firstNumber") Integer firstNumber, 
        @PathVariable("secondNumber") Integer secondNumber) {
            MathService = new MathServiceImpl();
            Integer difference = mathService.SubtractNumbers(firstNumber, secondNumber);
            return new ResponseEntity(difference, HttpStatus.OK);
    }
}

If we run this locally and call it from a browser, the output is shown in Fig. 2.

AddNumbers
Fig. 2

The results are the same as before our refactoring. The difference is that splitting into a service and interface provides more flexibility and maintainability. That may not be obvious for this simple contrived example; but it is very true for a complex workflow with external dependencies.

Dependency Injection

Conclusion

In this article, I will show you how to separate logic into its own service.

In the next article, I will show you how to simplify this using Dependency Injection.

You can find the sample code here.


Joe Guadagno on Leadership

Comments [0]

Episode 720

Joe Guadagno on Leadership

Joe Guadagno describes the difference between Leadership and Management, what it takes to be a good leader, and how to cultivate leadership among your team.


I first discovered Neil Gaiman's "The Sandman" comic decades ago and I have read it twice. In recent years, the series has gained more attention with the release of a Netflix TV series and an Audible radio play, both of which are excellent.

After consuming The Sandman saga in multiple media, I stumbled upon "Sandman: Book of Dreams" - a 1996 collection of short stories set in the universes occupied by The Sandman. A different writer delivers each story, and the collection is edited by Neil Gaiman and Ed Kramer. Gaiman introduces each story with a brief blurb about the author and his relationship thereto.

"The Sandman" is the story of Morpheus, the god-like creator of and lord of the realm of dreams into which each of us slips every night. But it is also the story of the other worlds in which Morpheus interacts and the characters he meets along the way. This collection visits many of those worlds and many of those characters.

I had read some of the authors (Gene Wolfe, Susanna Clarke) and I knew the names of others I had not read (Clive Barker), but I was unfamiliar with most of the writers in this collection.

The book contains a variety of stories, ranging from folk tales to mysteries to horror stories and monster stories; but one thing I can say for them all; they are all dark. Sometimes very dark.

I have not decided if the following are the best stories in "Book of Dreams", but they were the most memorable to me.

"Splatter"

A disturbing tale of the Cereal Convention told from the point of view of a new character.

"Seven Nights in Slumberland"

An interesting crossover between Morpheus and Little Nemo. Little Nemo was the star of "Little Nemo in Slumberland" - a pioneering comic strip from the early twentieth century, which chronicled a young boy's vivid dreams. In this story, those dreams take place in Morpheus's kingdom.

"Escape Artist"

The "Origin Story" of Wanda - one of the more colorful characters in the Sandman saga.

"An Extra Smidgeon of Eternity"

A man is frustrated by the timing of his death because it means he will not hear the end of a story he was enjoying. (Death, as Sandman fans know, is the older sister of Morpheus)

"The Mender of Broken Dreams"

One of Morpheus's subjects wonders about his origins and his own existence. And receives an answer.

"Ain't You 'Most Done"

In the first dream of his life, a rich man gets to do what he always dreamed of doing before he dies.

"Stopp't Clock Yard"

Two London magicians anger Morpheus and he takes his revenge.

I do not know if those unfamiliar with the world of The Sandman will enjoy this collection. I suspect they will. Each story is well-written and coherent, if a bit fantastical. But their connection with the universe and characters Gaiman created make them more special to those of us who count ourselves as fans.

 


The saga moves forward in "Lord of Chaos", the sixth volume in Robert Jordan's "Wheel of Time" series.

The entire world grows hotter, thanks to the growing power of The Dark One;

Rand, the Dragon Reborn, raises and trains an army in preparation for The Final Battle with The Dark One. But, Rand struggles to control the voice of a long-dead mad king inside his mind.

The rift between factions of the mystical Aes Sedai sisterhood grows wider and more intense.

The female protagonists - in training to become Aes Sedai - get a promotion, including a huge one for Egwayne;

Jordan continues his world building and character evolution that made the previous novels successful; but it suffers from the same weakness as many of its predecessors: the story drags on much longer than it should.

As in the previous stories, a strong ending saves this one.


GCast 131:

Managing Secrets in an Azure Key Vault

Learn how to store and manage secrets in an Azure Key Vault using the Azure Portal.


Overview

In a previous article, I showed how to create a simple REST API in a Spring Boot application. I only covered the GET HTTP verb in that article. In this article, I will show how to implement other HTTP verbs and pass models to and from a REST service.

HTTP Supports the following verbs with a request: GET, POST, PUT, PATCH, and DELETE. These verbs map well to common data operations (GET to read data; POST to add data; PUT and PATCH to update data; and DELETE to delete data), but this mapping is not required and developers do not always adhere to it. One advantage a POST request has over a GET request is that you can send large and more complex data in the body of a POST request. A POST request often includes data in the JSON format in its body.

NOTE: The code for the previous article can be found at https://github.com/DavidGiard/Demo-Spring-App/releases/tag/Spring-Boot-REST-API

Models

A model is a class designed to hold data. When we create an instance of a model class, we can use that instance to represent a real or conceptual object and all the properties of that object.

In Java, a model class is created no differently than any other class. I like to create a folder named "models" in which to store model classes. Here, I will create two classes: GreetingInput and GreetingOutput, as shown below

GreetingInput.java:

package com.dgtest.models;
public class GreetingInput {
    public GreetingInput(String personName) {
        this.personName = personName;
    }
    public GreetingInput() {
    }

    private String personName;
    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}

GreetingOutput.java:

package com.dgtest.models;
public class GreetingOutput {
    public GreetingOutput(String greeting) {
        this.greeting = greeting;
    }
    public GreetingOutput() {
    }

    private String greeting;
    public String getGreeting() {
        return greeting;
    }
    public void setGreeting(String greeting) {
        this.greeting = greeting;
    }
}

GreetingInput has one property (personName), along with a setter and a getter for that property.

GreetingOutput has on (greeting), along with a setter and a getter for that property.

In addition, each class contains an overloaded constructor, along with one to set the property's value when an instance is created.

More Controller Methods / More Verbs

As you have probably guessed, we will pass an instance of the GreetingInput class as an input to our method, which will return an instance of the GreetingOutput class. Because we are dealing with HTTP, this data will be passed in JSON format and Java/Spring will automatically convert it into a POJO ("Plain Old Java Object").

Testing

My previous article handled HTTP GET requests; but Spring Boot can handle other HTTP verbs, such as POST, PUT, PATCH, and DELETE.

Add the following method to your controller to handle a POST request:

@PostMapping(path="hello", consumes="application/json", produces="application/json")
public ResponseEntity greetPersonPost(@RequestBody GreetingInput greetingInput) {
	String personName = greetingInput.getPersonName();
	String greeting = "Hello, " + personName;
	GreetingOutput greetingOutput = new GreetingOutput(greeting);
	return new ResponseEntity(greetingOutput, HttpStatus.OK);
}

There are four things to call out in this method:

NOTE: If you do not yet have a Controller method, see this article.

  1. The @PostMapping attribute
  2. The ResponseEntity return type
  3. The GreetingInput input parameter
  4. The @RequestBody attribute

@PostMapping attribute

Notice the @PostMapping attribute on the method. This tells Spring that it will run in response to an HTTP POST request. This attribute contains the following arguments:

  • path="hello": This identifies the relative path in the URL to which the POST request is submitted
  • consumes="application/json": This tells Spring to expect data in the body of the request to be formatted as JSON.
  • produces="application/json": This tells Spring to return response data in JSON format

The ResponseEntity return type

The method returns a ResponseEntity containing an object of type GreetingOutput. We created this class earlier. It has one property: greeting. Although the code generates an instance of a POJO, Spring will automatically convert the object to JSON when sending a response to the client, thanks to the produces argument in the @PostMapping attribute.

The GreetingInput input parameter

This method accepts an object of type GreetingInput - a class we created above that contains one property: personName. The client will supply this in JSON format and Spring will automatically convert it to a POJO, thanks to the consumes argument in the @PostMapping attribute.

The @RequestBody attribute

The greetingInput parameter is decorated with the @RequestBody attribute. This tells Spring to look for the value of the parameter in the Body of the HTTP request.

Calling the Method

The code in this method performs a similar function to the greetPerson method created in the previous article. The main difference is that we return an object, rather than a string.

To test the method, we run or debug it locally; then submit an HTTP POST request to http://localhost:8080/greetings/hello. The body of this request will contain a JSON object with the same properties as the greetingInput class, similar to the following:

{
    "personName" : "David"
}

You may choose your favourite tool to submit a POST request. I prefer PostMan, which is a free cross-platform tool that you can download from https://www.postman.com/downloads/

Fig. 1 shows the PostMan interface with the important values highlighted.

PostMan
Fig. 1

Run or Debug the application locally, then complete the following in PostMan:

At the HTTP verb dropdown, select "POST".

At the Request URL field, enter "http://localhost:8080/greetings/hello"

At the Request Body field, select the "raw" radio button and the JSON data format and enter the following text:

{
    "personName" : "David"
}

Click the [Send] button

You should see a response code of "Status: 200 OK" and the following value in the response body:

{
    "greeting": "Hello, David"
}

If you receive an error, verify that the app is running and that your URL is correct. Pay attention to the route and the port number. You may also get a clue in the error response message.

Other HTTP Verbs

The process is nearly identical in handling HTTP PUT, PATCH, and DELETE requests. Spring provides the @PutMapping, @PatchMapping, and @DeleteMapping attributes, which work in a very similar way to the @PostMapping attribute.

Conclusion

In this article, I showed you how to implement a method to handle HTTP POST requests from a Java Spring application. You can see the completed code at https://github.com/DavidGiard/Demo-Spring-App/releases/tag/Handle-HTTP-POST-Requests


Overview

In a previous article, I showed how to use start.spring.io to create a new Spring Boot application. In this article, I will show how to extend that application and create a REST API.

REST (short for "Representational State Transfer") is an architectural pattern that allows you to access, create, and manipulate backend resources over HTTP.

Spring Boot uses the Model-View-Controller (MVC) pattern for web apps, which separates the business logic, the data model, and the user interface into different components. A web service does not have a user interface, but it returns data to the client - typically as JSON - and we can think of this as the interface.

In the demo described here, we will begin with the sample application created using start.spring.io in the previous article.

Controllers

In an MVC application, a Controller contains or calls the business logic. By convention, we add controllers to a folder named "controllers".

Create a file named "GreetingsController.java" with a GreetingsController class in the controllers folder. Add a method named "greet" that accepts no parameters and returns a String, as in the following code:

public class GreetingsController {
    public String greet() {
        return "Hello";
    }
}

Your project should look similar to Fig. 1.

Controller Class
Fig. 1

Identify this class as a controller by decorating the class with the @RestController attribute.

We want to map the greet method to a route, so that users can access it via HTTP. A route specifies the relative URL that maps to a specific method in a controller. An example will help to demonstrate this.

To make Spring recognize this as an MVC controller, decorate the class with the @RequestMapping attribute. Add the parameter "/greetings" to the RestController attribute to indicate the route path of the controller.

We can then make the greet method accessible via HTTP by decorating this method with the @GetMapping attribute with the parameter "greet". GetMapping makes this method available via the HTTP GET verb.

Here is the code, as described so far:

package com.dgtest.dgtest.controllers;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("greetings")
@RestController
public class GreetingsController {
    @GetMapping("greet")
    public String greet() {
        return "Hello";
    }
}

When you run or debug this application locally, it displays output similar to that in Fig. 2.

Run Output
Fig. 2

Notice that it is running locally on port 8080 (I have highlighted the relevant output). Therefore, the host will be http://localhost:8080.

To access methods in the controller class, we append the RequestMapping parameter (in this case "greetings") to the URL. To call a specific method via HTTP GET, we append the GetMethod parameter (in this case "greet" for the greet() method.

So, when the application runs locally, we can call the greet method by sending an HTTP GET request to http://localhost:8080/greetings/greet

Try this by opening a browser and navigating to http://localhost:8080/greetings/greet. This sends an HTTP GET request to that URL.

The string "Hello" should appear in the browser, as shown in Fig. 3.

Http Get
Fig. 3

NOTE: If I deploy this to another web server, the host part of the URL will change. For example, I could create a site named "https://davidsawesomewebaapi.azurewebsites.net" and the URL mapped to this method would be https://davidsawesomewebaapi.azurewebsites.net/greetings/greet

Returning a ResponseEntity

In the example above, we returned a string. This is useful, but HTTP has the ability to return metadata with that string. For example, we may want to know where the request came from or any error information. Information like this can be included in the Header of an HTTP response. Spring provides a ResponsEntity object that returns a full HTTP response to the client, allowing you to customize it, as needed.

The code is shown below:

@GetMapping("greet")
public ResponseEntity greet() {
 return new ResponseEntity("Hello", HttpStatus.OK);
}

A ResponsEntity contains an object within it, which will be the body that is returned in the HTTP response. In this case, the body will be the string ("Hello") that we returned to the client in the example above. The difference is that we are returning a full HTTP response. The HttpStatus.OK represents the value 200, which is a standard HTTP response code, indicating that everything worked correctly. Later, when I cover error handling, we can return a different HTTP response code and more information about the error. We can also manipulate the headers of the response before returning it, but that is beyond the scope of this article.

Passing parameters to a REST request

There are a number of ways to pass parameters to a GET request. Spring's MVC capabilities provide a simple way to add extra information to a URL. For example, we may want to create a method similar to greet that accepts a person's name as an argument and call it using a local URL similar to the following:

http://localhost:8080/greetings/greet/David

Currently, the greet method does not accept parameters, so we will create in our controller a new method named "greetPerson" that accepts the personName parameter, as in the following listing:

public ResponseEntity greetPerson(String personName) {
 String greeting = "Hello, " + personName;
 return new ResponseEntity(greeting, HttpStatus.OK);
}

We must tell the method where to find the value of this parameter. In this case, it is part of the URL, so we modify GetMapping argument and we decorate the personName parameter with the @PathVariable attribute, as shown below:

@GetMapping("greet/{personName}")
public ResponseEntity greetPerson(@PathVariable("personName")String personName) {
 String greeting = "Hello, " + personName;
 return new ResponseEntity(greeting, HttpStatus.OK);
}

The curly braces around personName in the GetMapping path indicate that this is a variable value provided at runtime. The is stored in a temporary variable named "personName" and the PathVariable argument indicates that we should pull the personName value from the URL and assign it to the personName parameter.

Now, when we navigate to http://localhost:8080/greetings/greet/David in a browser, it returns the results shown in Fig. 4.

Parameter
Fig. 4

Conclusion

In this article, you learned how to create a simple REST API in a Spring Boot application. I only covered the GET HTTP verb. In the next article, I will show how to implement other HTTP verbs and pass models to and from a REST service.

You can find the source code for this article at https://github.com/DavidGiard/Demo-Spring-App/releases/tag/start.spring.io


Episode 719

Don Miller on Headless Content Management Systems

Don Miller describes how he and his team are using Headless Content Management Systems (CMSs) to generate fast, secure web sites from existing CMS systems data.

Links:

The Beginner’s Guide to Headless CMSes

 


Listening to Al Dimeola's albums does not prepare you for seeing Al Dimeola perform live. Nothing prepares you for seeing him perform.

You cannot appreciate his technical excellence until you see his fingers work their magic on his guitar. He brings to his craft the perfect combination of speed and emotion.

Friday night at the City Winery, Mr. Dimeola delighted the crowd with his musical prowess. He is known for a number of different styles, including a significant stint playing jazz fusion - both solo and with the legendary supergroup Return To Forever. But, on this night, he brought only his acoustic guitar. 

For me, his best work was playing Latin music and he served up plenty Friday evening with songs from and inspired by the music of Spain, North America, and South America. He mixed in some classical sounding music. Nearly everything he played was his own composition, but he did treat the audience to an arrangement of Lennon and McCartney's "Norwegian Wood" and at least one Astor Piazzolla tango.

Al was joined by two excellent percussionists, who complemented his music without overpowering it.

I was fortunate to have front row seats and I sat in awe as fingers flew across strings for nearly two hours. He makes a six string guitar sound like a 12-string guitar - sometimes like two 12-string guitars.

He closed with his classic "Mediterranean Sundance" before thanking the audience and exiting without an encore. I could have stayed and heard more, but I did not leave unsatisfied.

I would love to see him again now that I am prepared.

 

Photos


Rocky Mountain Rhapsody

Comments [0]

It has been years since I have been to Colorado, and I have never ventured outside of Denver until this past Labor Day weekend. Two vacation days extended the holiday into a 5-day holiday.

We arrived Thursday evening, greeted some old and new friends, and talked for a bit before resting in preparation for a busy Friday.

First on the agenda (after breakfast, of course) was a guided walking tour of downtown Denver. It was a hot day and my friends only lasted about half of the 3-hour tour; but I was happy to complete it and meet up with them later. We did not go into any buildings but received a good overview of the history and architecture of the city, walking from the state capitol to Civic Center Park to the theatre district to the art museum. It was a nice introduction to the city. In the afternoon, we visited the home of Molly Brown, the "unsinkable" woman who showed bravery and generosity when she survived the sinking of the Titanic. Brown was the estranged wife of a successful miner and spent most of her life in Denver. In the late afternoon, my friend Jerry drove down from his home in the mountains to spend an hour catching up. We have worked together at two different jobs, but rarely see one another face-to-face, so this was a treat.

Saturday morning, we headed west, stopping first at Red Rocks - an amphitheater carved out of the mountain stones from which it derives its name. We arrived too late Thursday to see Robert Plante and Allison Krauss and my friends were not fans enough to stay for the Nine Inch Nails concert in the evening; so, we contented ourselves with walking around the area and a walk through the small museum, which featured photos of the many stars who have performed in this famous venue. If I return to Denver, I hope to see a concert here.

We left Red Rocks and took I-70 toward Vail, stopping first for lunch at Idaho Spring - a small mining town that retains much of its history. A local historical group put on an entertaining skit about a 19th-century robbery next to the railroad tracks and a vintage engine. Our next stop was Georgetown because someone heard that John Denver thought it was the most beautiful town in Colorado. It was nice, but we just drove through, admiring the homes. 

Finally, we arrived in Vail in the late afternoon. This area is spectacular. While Denver is mostly flat, Vail is nestled within the Rocky Mountains which soar on every side. The town is a facsimile of an Alpine village with small shops and cafes along pedestrian roads. We walked these roads and hiked some of the trails along the rivers and mountains and sat at outdoor cafes enjoying the food and watching the world go by. On the final morning, we took the gondola cable car up to the top of a local mountain. From there, the views were spectacular, and I enjoyed watching mountain bikers risk injury as they sped down the mountain at top speeds.

Traffic back to Denver was heavy, as one would expect on a holiday weekend. The one thing I had hoped to do was attend the Colorado Rockies game at Coors Field - one of the few MLB stadiums I had yet to visit. We arrived at our hotel 2 hours after the game started, but I managed to go to the game and watch the final three innings. Check that one off!

Our flight home Tuesday was not until the evening. We began the day at a breakfast place recommended by my son, who attended a Denver wedding earlier this summer. After breakfast, we visited the Denver Art Museum, which has a wide variety of paintings, sculptures, and other forms of art; but is most memorable for its collection of US Southwestern art and Native American art. 

Colorado has astonishing natural beauty - much more than my current home state of Illinois. However, the cultural attractions of Denver are not as compelling as those in Chicago. In addition, Denver suffers from an inordinate number of homeless people. This did not bother me (I encounter the homeless often in Chicago) until our final day when a man began harassing us and went so far as to throw things at the back of my head as I was walking away. Fortunately, no harm came of it. Vail was beautiful but the cost of everything from hotels to meals to lift tickets will keep me from returning too frequently.

It was a good time to go to Colorado. Temperatures were high in Denver, but pleasant in the mountains and we saw no hint of rain or overcast skies. Traveling with friends made it more special and exploring new places is always a pleasure.

 


<< Older Posts | Newer Posts >>