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.

 


Episode 718

Michael Richardson on Yarn Berry

Michael Richardson describes how to use the Yarn Berry package manager to add functionality to your JavaScript applications.


August 2022 Gratitudes

Comments [0]

8/9

Today I am grateful to see the Fabulous Thunderbirds in concert last night.

 

8/10

Today I am grateful that I can connect with friends remotely.

 

8/11

Today I am grateful to hear live music at a new park in the West Loop yesterday.

 

8/12

Today I am grateful for a free concert every Thursday evening at Willie Dixon's Blues Heaven Foundation

 

8/13

Today I am grateful for a walk along the Chicago Riverfront last night

 

8/14

Today I am grateful to attend an exciting White Sox - Tigers game last night with family and friends.

 

8/15

Today I am grateful for a visit from my sisters this weekend.

 

8/17

Today I am grateful I can stream movies on demand

 

8/18

Today I am grateful for my annual physical exam.

 

8/19

Today I am grateful to present at the Chicago .NET User Group last night at their first in-person event in over 2 years.

 

8/20

Today I am grateful for wood-fired pizza

 

8/21

Today I am grateful to see "Cruel Intentions: The Musical" on my first visit to the Chopin Theater last night.

 

8/22

Today I am grateful I was able to help an 85-year-old lady resolve her computer issues yesterday.

 

8/23

Today I am grateful for a conversation with Kira yesterday.

 

8/24

Today I am grateful to schedule the travel for 2 vacations this fall

 

8/25

Today I am grateful for dinner with Richard last night in Chinatown

 

8/26

Today I am grateful for my first visit to the Chicago Magic Lounge

 

8/27

Today I am grateful to sleep in for the first time in weeks

 

8/28

Today I am grateful to see The Wallflowers in concert last night.

 

8/29

Today I am grateful to Tim, who picked me up, drove me to my car, and jump-started my dead battery yesterday.

 

8/30

Today I am grateful for a new pair of shoes - a gift from my son.

 

8/31

Today I am grateful for a virtual happy hour yesterday with Tze Lin, Hattan, and Ernesto.

 

9/1

Today I am grateful to update dozens of old passwords and store them in a password manager this week.

 

9/2

Today I am grateful to arrive safely in Colorado last night

 

9/3

Today I am grateful for:

-a guided walking tour of Denver

-a visit to (the unsinkable) Molly Brown home

-hanging out with Jerry in the afternoon

 

9/4

Today I am grateful for:

- a walk around the Red Rocks Amphitheatre

- a visit to Idaho Springs

- my first taste of Colorado-style pizza

 


The Wallflowers Jakob Dylan will never be the prolific songwriter that his father was. His father, of course, is legendary singer-songwriter Bob Dylan, who revolutionized popular music in the 1960s and won a Nobel Prize in the 2010s. But Jakob shares much of his father's songwriting talent and Bob's habit of building complex lyrics atop simple melodies. And, unlike his father, Jakob can carry a tune.

The younger Dylan is currently 52 years old and has been leading his band The Wallflowers for decades. Saturday evening, he brought The Wallflowers to the SPACE nightclub in Evanston for the first of two shows. The club was packed to see a band that typically plays much larger venues and the Wallflowers did not disappoint.

Dylan's music ranges from folk to rock to hymns to anthems to ballads and he played them all to the delight of the audience. He chatted with the crowd and made eye contact, something his father stopped doing years ago.

I have been to SPACE many times over the years. This was the first time they removed the tables and chairs and hosted a standing-room-only show. This was less than ideal, but it was likely the only way to entice this band to play in such a small room. Arriving early and standing by the stage provided a unique opportunity to relate to Jakob's emotions during the performance. He had the habit of picking out someone in the audience and singing directly to them during each number.

The audience loved hits like "One Headlight" and "6th Avenue Heartache", as well as a rendition of Tom Petty's "The Waiting", but the band performed every song with enthusiasm and received with pleasure.

If I had one complaint about the show it was the lack of lighting on the stage. Some musicians remained in shadow the entire set and no spotlight shone on anyone - even on Dylan.

Local artist Nathan Graham was an excellent choice as a warmup act, performing his original soulful melodies while accompanying himself on guitar.

I originally bought tickets to see The Wallflowers at SPACE in 2011; but an illness in the band forced them to cancel that show. Fortunately, they rescheduled over a year later and I was able to see them for the first time.

It was worth the wait.


GCast 130:

Creating an Azure Key Vault

Azure Key Vault allows you to securely store certificates, encryptions keys, and other secrets. In this video, you will learn how to create an Azure Key Vault.


Overview

Spring is a framework designed to build Java applications quickly. Applications built with Spring are flexible because the framework supports many other frameworks, such as Hibernate, Struts, and EJB.

Start.Spring.io

The folks who created Spring provided a page to quickly bootstrap an application. To create your first Spring app, open a browser and navigate to https://start.spring.io/, as shown in Fig. 1.

spr01-StartSpringIo
Fig. 1

At the "Project" field, select the Build Automation tool you want your app to use. Maven and Gradle are both popular tools, so your choice may come down to which tool you and your team are most familiar with.

At the "Language" field, select your language of choice. Java, Kotlin, and Groovy all compile to Java bytecode and run on the Java Virtual Machine (JVM), so they are all compatible with Spring.

At the "Spring Boot" field, select the version of Spring you wish to use. Generally, you should select the latest version that is not a "SNAPSHOT" version. SNAPSHOT versions are created for development and testing and may not be as stable.

At the "Group" field, enter a group ID for the project. This is similar to a project's default namespace in .NET.

At the "Artifact" field, enter a descriptive name for the project.

The "Name" field defaults to your Artifact ID. It is usually fine to accept this default.

At the "Description" field, enter a brief description of your project.

The "Package name" field defaults to the Group ID, followed by ".", followed by the Artifact ID. It is usually fine to accept this default.

At the "Packaging" field, specify whether you want Spring to generate JAR files or WAR files when packaging the application. A WAR file is only appropriate for a web application, while a JAR file can be used for any application. This article explains the differences between the two. I typically select JAR files.

At the "Java" field, select the version of Java your application will use. For a new project, you will likely want to choose the latest Long Term Release (LTS) version of Java. As of this writing, that is version 17. Exceptions would be

  • You need a feature available in a more recent version
  • You are creating an application that must be compatible with a project built using an older version

Click the [ADD DEPENDENCIES] button to add more dependencies to your application. A dialog displays a list of available dependencies, as shown in Fig. 2.

spr03-AddDependencies
Fig. 2

For example, you can select "Spring Web" if you intend to build a REST API. You can always add dependencies after the project is created, but this wizard makes it easier to do so.

Click the [GENERATE] button to generate the project. The wizard will generate all the necessary folders and files, compress them into a ZIP file, and download that file.

Extract the ZIP file into a location on your hard drive. The structure will look similar to that shown in Fig. 3.

spr03-ProjectStructure
Fig. 3

Open the project in your favourite Integrated Development Environment (IDE). This is a good starting point for your application.

Conclusion

In this article, I showed you how to create a new Spring application using start.spring.io.

You can find the final code here.


Application Insights is a scalable Azure data store and service that is ideal for storing monitoring information of all kinds.

To install an Application Insights service, navigate to the Azure Portal then, click the [Create a resource] button (Fig. 1), and search for Application Insights (Fig. 2) to display the "Application Insights" start page, as shown in Fig. 3.

Create Resource Button
Fig. 1

Search for Application Insights
Fig. 2

Applicatino Insights Description Page
Fig. 3

Click the [Create] button to open the "Application Insights" creation dialog, as shown in Fig. 4.

Create Application Insights Blade
Fig. 4

At the "Subscription" dropdown, select the Azure subscription in which to create the Application Insights service. Most of you will have only one subscription.

At the "Resource Group" dialog, select a resource group in which to store the Application Insights service or click "Create new" to create a new one.

At the "Name" field, enter a unique name for your Application Insights service.

At the "Region" field, select a region into which to deploy this service. Consider the locations of the services that will write to this data store in order to reduce latency.

At the "Resource Mode" field, select "Workspace-based".

At the WORKSPACE DETAILS fields, select the defaults.

Click the [Review + create] button to display the "Review + create" tab, as shown in Fig. 5.

Create Application Insights Blade
Fig. 5

This tab will display any validation errors. Correct them and click the [Create] button to create the Application Insights service.

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

Application Insights Overview Blade Fig. 6

The "Overview" blade contains the "Instrumentation Key" (Fig. 7). This is an important piece of information that is often used to connect your application to Application Insights.

Instrumentation Key
Fig. 7

In this article, you learned how to create an Azure Application Insights service.


Episode 717

Michael Scherotter on Galleryst

Using tools from Adobe and Microsoft, as well as open source libraries, Michael Scherotter has created a portal to display photography and artwork within a virtual gallery. He describes how he created galeryst.com and how to use it.


Malcolm sighed. "Do you have any idea," he said, "how unlikely it is that you, or any of us, "will get off this island alive?"

Michael Chrichton was already a bestselling author when he published "Jurassic Park" in 1990. But none of his novels and few novels by other authors had the cultural impact of this one. Steven Spielberg adapted this book into an excellent movie in 1993, which spawned five sequels of varying quality but undeniable popularity.

JP is the story of a billionaire who hires a group of scientists to clone extinct dinosaurs to create a zoo / theme park on a remote island off the coast of Central America. They are confident that their redundant systems have eliminated any risk. Of course, they are wrong; and everything soon goes tragically wrong.

It is a story of the consequence of hubris and the arrogance of man playing the part of God.

But it is not only a morality tale. It is an action story and an adventure story and a horror story and a monster story and a story of corporate espionage. Chrichton - a former MD - goes to great lengths to make the science plausible, even if we are unlikely to resurrect extinct creatures any time soon in this world.

If the book has a weakness, it is the single-dimensional characters; but this did not bother me. The dinosaurs are the stars here.

As the climax approached, I felt the fear and the stress, and the danger experienced by both heroes and villains as the dinosaurs closed in on them. I related to Alan and Ellie and Malcolm and Tim and Lex and Hammond and their approach to the project.

Spielberg made dinosaurs a cultural phenomenon; but he would not have done so without this book.


CruelIntentions"Cruel Intentions: The 90s Musical" is a play based on a movie I never saw, which is based on a book I never read. I did not know what to expect.

In preparation to see the live show at the Chopin Theatre Saturday evening, I watched the 1999 movie on Friday. I was underwhelmed by the campy dialogue and mediocre acting, despite the presence of stars Reese Witherspoon and Sarah Michelle Gellar and semi-star Ryan Phillipe.

But the things that brought down the movie worked in the live production. The musical version took itself far less seriously than the film, making the campy dialogue seem more appropriate, often gaining an audience laugh where the same line inspired an eye roll while watching the movie.

"Cruel Intentions" is the story of Sebastian Valmont and Kathryn Merteuil – entitled step-siblings enrolled at an exclusive prep school in Manhattan. They are so privileged and amoral that they place a bet on whether Sebastian can deflower the virginal daughter of the school's new headmaster.

Manipulation, false promises, and general sociopathy ensue; but all this is interrupted periodically by cast members bursting into hit songs from the 1990s. In the stage version, the plot takes a back seat to the music. Feeling confused or angsty? Sing REM's "Losing My Religion"; Feeling sexy? Belt out Marcy Playground's "Sex and Candy"; a little self-hate can be expressed with a raucous rendition of Garbage's "I'm Only Happy When It Rains"; Use the Back Street Boys' "I Want it That Way" to flirt. The lyrics do not always fit the situation exactly, but the mood does. And somehow it works.

It works because the cast has fun with it. And so did I.


Overview

Java is a popular language for developing applications on a variety of platforms. In order to create applications, you must first install Java on your machine or virtual machine. This article will walk you through the steps to install Java on Windows 10 or 11.

Is Java Already Installed?

You can test whether Java is currently by opening a command prompt, typing "java -version", and pressing ENTER.

If Java is installed, information about the current Java version will display. If Java is not installed, you will see a message similar to the following:

'java' is not recognized as an internal or external command, operable program or batch file.

A Note About Versions

You can choose from a number of available versions of Java. Generally, I recommend installing the latest Long Term Support ("LTS") version of Java. You may want to install a more recent non-LTS version of Java if you require a feature that is only available in that version. Also, the application on which you are working may require a specific version of Java.

As of this writing, Java 18 is the most recent version of Java, but it is not LTS. The available LTS versions are Java 8, 11, and 17.

This article describes the current LTS versions of Java.

Download the installation Executable

To download the installation files, navigate to https://www.oracle.com/java/technologies/downloads/. This page is shown in Fig. 1.

ij01-JavaDownloads
Fig. 1

In the center of the page are tabs for "Java 18" and "Java 17", as shown in Fig. 2.

ij02-JavaVersionTabs
Fig. 2

Currently, Java 18 is selected, so I will select "Java 17" because I want the LTS version. The "Java 17" tab is shown in Fig. 3.

ij03-Java17Downloads
Fig. 3

Lower on the page are tabs for different operating systems (Linux, macOS, and Windows), as shown in Fig. 4.

ij04-OSTabs
Fig. 4

Currently, Linux is selected; but I want to install this on Windows, so I will click the Windows tab. The Windows tab is shown in Fig. 5.

ij05-Java17WindowsDownloads
Fig. 5

NOTE: As of this writing, you navigate directly to the Java 17 Windows tab with the following URL: https://www.oracle.com/java/technologies/downloads/#jdk17-windows

The tab lists the available downloads relevant to Windows developers for Java 17.

Click jdk-17_windows-x64_bin.exe to begin downloading it.

Install It!

When the exe file finishes downloading, click the file to launch the install wizard.

The Installation Wizard displays, as shown in Fig. 6.

ij06-InstallWizard
Fig. 6

Click the [Next>] button to advance to the next page of the Wizard, as shown in Fig. 7.

ij07-Folder
Fig. 7

If desired, click the [Change…] button to change the folder in which Java is installed. Usually, there is no reason for doing this.

Click the [Next] button to begin installing Java.

When installation completes, a confirmation dialog displays, as shown in Fig. 8.

ij08-JavaInstalledConfirmation
Fig. 8

Click the [Close] button to close the Installation Wizard.

Verify Installation

After installing Java, it is a good idea to verify that it was installed correctly. The simplest way to do this is to open a command prompt, type "java -version", and press ENTER.

If Java is installed, information about the current Java version will display. If Java is not installed, you will see a message similar to the following:

'java' is not recognized as an internal or external command, operable program or batch file.

Note that you must open this command prompt after installing Java. If you opened the prompt before installation, it will not know that Java is installed.

Conclusion

In this article, I showed you how to install the latest LTS version of Java on your Windows machine.


Overview

By default, Azure App Services will not enforce any authorization for your Web Apps. You can implement authorization within your code, or you can configure an Identity Provider to perform authentication and authorization for you. In this article, I will walk you through the process of configuring Azure Active Directory as an identity provider. This will force anyone to log in with an Account in or registered in Azure Active Directory before accessing your website.

Register Identity Provider

The first step after you create your web app is to register an Identity provider.

Navigate to the App Service, as shown in Fig. 1.

waip01-OverviewBlade
Fig. 1

Select "Authentication" from the left menu to open the Authentication blade, as shown in Fig. 2.

waip02-AuthenticationBlade
Fig. 2

A new app will contain no Identity Providers. Click the [Add identity provider] button (Fig. 3) to open the "Add an identity provider" dialog, as shown in Fig. 4.

waip03-AddIdentityProviderButton
Fig. 3

waip04-AddAnIdentityProviderDialog
Fig. 4

Completing this dialog will create a new App Registration in Azure Active Directory. From the "Identity provider" dropdown, select "Microsoft". The rest of the dialog will display prompts related to this provider, as shown in Fig. 5.

waip05-AddAnIdentityProviderDialogBasics
Fig. 5

At the "App registration type" prompt, select the "Create new app registration" radio button.

At the "Name" prompt, enter a unique name for the app registration. This will default to the name of your web app.

At the "Supported account types" prompt, you can choose to restrict access to only accounts in the current Active Directory tenant, to accounts in this and other tenants, and/or to personal accounts registered with Active Directory.

At the "Restrict access" prompt, select the "Require authentication" radio button.

The "Unauthenticated requests" prompt allows you to select the HTTP response returned when a user fails to authenticate. Most of these are appropriate for APIs. For a website, select the "HTTP 302" radio button.

Click the [Add] button to create a new App Registration in Azure Active Directory and return to the "Authentication" blade, as shown in Fig. 6.

waip06-AuthenticationBlade
Fig. 6

You should now see your newly created Identity Provider listed.

Grant access to roles

After registering your application, the next step is to grant access to specific users or roles within your app. By default, each App Service contains a couple of dozen roles. Adding an account to a role permits them to perform certain activities, such as viewing or updating your site.

To assign an account to a role, click the [Access control (IAM)] button (Fig. 7) in the left menu to open the "Access control (IAM)" blade, as shown in Fig. 8.

waip07-IAMButton
Fig. 7

waip08-IAMBlade
Fig. 8

On the "Access control (IAM)" blade, click the [Add role assignment] button (Fig. 9) to open the "Add role assignment" page, as shown in Fig. 10.

waip09-AddRoleAssignmentButton
Fig. 9

waip10-AddRoleAssignmentRole
Fig. 10

On the "Role" tab, select the "Reader" role; then, click the [Next] button to advance to the "Members" tab, as shown in Fig. 11.

waip11-AddRoleAssignmentMembers
Fig. 11

Click the "Select members" link (Fig. 12) to open the "Select members" dialog, as shown in Fig. 13.

waip12-SelectMembersLink
Fig. 12

waip13-SelectMembers
Fig. 13

Search for an Active Directory account, as shown in Fig. 14 and 15.

waip14-SelectMembers
Fig. 14

waip15-SelectMembers
Fig. 15

Click the [Select] button to add this account to the "Reader" role. You will return to the "Add role assignment" page, as shown in Fig. 16. The account will now be listed under "Members".

waip16-AddRoleAssignment
Fig. 16

Click the [Review + assign] button to advance to the "Review + assign" tab, as shown in Fig. 17.

waip17-ReviewAndAssign
Fig. 17

Click the [Review + assign] button to save your changes.

Conclusion

In this article, you learned to configure Azure Active Directory as an Identity Provider for an Azure Web App. The steps above will create a new App Registration, which you can view from the "App Registration" blade of Azure Active Directory, as shown in Fig. 18.

waip18-AppRegistration
Fig. 18

You have the option to create and configure the App Registration yourself; but the steps describe here take care of much of the configuration for you.


Overview

You can use Visual Studio Code to create applications in a number of languages. Many of those applications can run in Microsoft Azure App Services. This article will show you how to deploy a web application from Visual Code to an Azure App Service.

## Install VS Code Azure App Service Extension

Before you begin deployment, you must install the Azure App Service Extension. Click the Extensions icon (Fig. 1) in the left sidebar and search for the Azure App Service extension from Microsoft, as shown in Fig. 2.

vcas01-ExtensionsIcon
Fig. 1

vcas02-InstallExtension
Fig. 2

If this extension is not yet installed, a [Publish] button will display. Click this button to install it.

Create a Web App

You can create a web app using ASP.NET, ASP.NET Core, Java, Ruby, Node.js, PHP, or Python. Visual Studio Code has extensions to support each of these. The language and framework are not relevant to this article, as the steps are the same.

Create a New Azure App Service

Before you deploy your code, you will need to Create a New Azure App Service to Host your Web App.

With your web application code open, click the Azure icon (Fig. 3) in the left sidebar and expand the Azure subscription to which you want to deploy and the "App Services" group within that subscription, as shown in Fig. 4.

vcas03-AzureIcon
Fig. 3

vcas04-SubscriptionAppService
Fig. 4

You will need an Azure Web App to host your code. If you have not yet created a Web App, you can create one now by right-clicking the "App Services" node and selecting "Create New Web App.." from the context menu, as shown in Fig. 5.

vcas05-AppServiceMenu
Fig. 5

Enter a globally unique name for this Web App, as shown in Fig. 6, and press ENTER.

vcas06-WebAppName
Fig. 6

Select the runtime stack from the list of supported stacks and press ENTER. In Fig. 7, I selected Node 16 LTS because I wrote my application in Node. The language in which you wrote your app will determine your selection.

vcas07-RuntimeStack
Fig. 7

At the "Select a pricing tier" prompt, select the tier to which you want to deploy your Web App, as shown in Fig. 8. More powerful tiers will be more robust and reliable, but more expensive.

vcas08-PricingTier
Fig. 8

After a few minutes, an Empty Web App is created. You can now deploy your application to this Web App by clicking the [Deploy] button on the confirmation dialog, as shown in Fig. 9.

Deploy App to Azure App Service

You can now deploy your application to this Web App by clicking the [Deploy] button on the confirmation dialog, as shown in Fig. 9.

vcas09-WebAppContextMenu
Fig. 9

The prompt in Fig. 10 displays.

vcas10-DeployTo
Fig. 10

Select the folder containing your application's code from the list or click browse to find it on your computer.

After you select the code location, the warning in Fig. 11 displays.

vcas11-AreYouSure
Fig. 11

Click the [Deploy] button to begin deploying your code to the Azure App Service.

You will receive the confirmation message shown in Fig. 12 when the deployment succeeds.

vcas12-WebAppCreated
Fig. 12

You can now browse your web site or manage the App Service in the Azure Portal.

Conclusion

In this article, I showed how to deploy a web application quickly and easily from Visual Studio Code.


Episode 716

Darryl Hogan on Accelerating Technologies for Nonprofits

Darryl Hogan discusses how technologists can help nonprofit organizations with their technical issues, such as building applications, databases, web sites, or infrastructure.


With "The Final Cut", Michael Dobbs concludes his "House of Cards" trilogy, and he does so in dramatic fashion.

This story begins with a flashback to a young Francis stationed in Cyprus in the British Army, assigned to fight the Cypriots in their battle for independence. Urquhart committed and covered up a war crime that no one has since discovered. Decades later, Urquhart faces a new challenge in Cyprus, which gives him the opportunity to rise above the public's criticism and save the day. He is decisive and forceful and determined to achieve victory at all costs; and he knows that he will be hailed a hero if he succeeds.

The series tells of the rise to power of British Prime Minister Francis Urquhart. TFC reveals what happens when Urquhart reaches the top and the world tires of him. Urquhart has been PM for 10 years and he is poised to eclipse Margaret Thatcher's tenure, but his popularity is declining. The public is clamoring for new blood and fresh ideas, while Francis is losing the support of many in his cabinet. Dobbs paints a picture of a ruthless man, terrified of losing his power. FU knows only politics and has no interests outside politics. He knows he will be lost if he loses the power he has built. "The Final Cut" is the story of a master Machiavellian, who rose to power by exploiting and destroying others and must use those same vile skills to stay on top. The PM always seems to come out on top at the expense of his rivals. Dobbs keeps us wondering if that will be the case this time.

This is a strong finish to an excellent trilogy.


Overview

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.

Each profile is associated with an email address. You can create a new email address at https://outlook.com

Creating a New Profile

To create a new profile, open Microsoft Edge and click the profile icon (Fig. 1) in the top right corner of the browser to launch the menu shown in Fig. 2. My profile icon has a photo of me, but yours may have your initials. You configured a default profile the first time you launched Microsoft Edge after installing it on this machine.

np01-ProfileIcon
Fig. 1

np02-ProfileMenu
Fig. 2

Click the [Add profile] menu option to open the "Add a profile" blade, as shown in Fig. 3.

np03-AddProfileDialogue
Fig. 3

The "Welcome to Microsoft Edge" screen displays, as shown in Fig. 4.

np04-SignIn
Fig. 4

Click the [Sign in to sync data] button to display, the "Let's get you signed in" dialog, as shown in Fig. 5.

np05-SignIn
Fig. 5

Enter your email address and click the [Sign In] button to display the "Enter password" dialog, as shown in Fig. 6.

np06-Password
Fig. 6

Enter the password associated with this email and click the [Sign In] button to display the "Help us protect your account" dialog, as shown in Fig. 7.

np07-ProtectAccount
Fig. 7

Re-enter your email address and click the [Next] button to display the "Enter your security code" dialog, as shown in Fig. 8; then check your email for a message with a 6-digit code.

np08-SecurityCode
Fig. 8

Enter the 6-digit code from the email and click the [Next] button to display the confirmation dialog, as shown in Fig. 9.

np09-UseWindowsHello
Fig. 9

Click the [OK] button to close this dialog and display the "Windows Security" dialog, as shown in Fig. 10.

np10-EnterPIN
Fig. 10

Enter the PIN you use to unlock this Windows workstation. click the [OK] button to display the "Add details" dialog, as shown in Fig. 11.

np11-AddDetails
Fig. 11

Enter your name, birth date, and country in the appropriate fields and click the [Next] button to display the "Please confirm your age" dialog, as shown in Fig. 12.

np12-ConfirmAge
Fig. 12

Click the [Next] button if your birth date is displayed correctly and click the [Next] button to display a confirmation dialog, as shown in Fig. 13. If your birth date is not displayed correctly, click the [Back] button and correct it.

np13-Confirm
Fig. 13

Click the [Confirm and start browsing] button to begin using this new profile.

Now that you have create the profile, you can switch between profiles by clicking the profile icon at the top right of the Edge browser, as shown in Fig. 14.

np14-ProfileMenu
Fig. 14

Managing a Profile

You can make changes to your profiles from the Edge Settings page. Open the Edge Settings page by clicking the Ellipsis in the top right corner of the browser and selecting "Settings" from the dropdown menu, as shown in Fig. 15.

np15-BrowserMenu
Fig. 15

The "Settings" page displays, as shown in Fig. 16.

np16-ProfileSettings
Fig. 16

If the "Profiles" tab is not selected, select it from the left sidebar. The current profile is highlighted, but you can view your other profiles in the "More profiles" section below. Click the [Switch] button next to one of these profiles to view the settings associated with it.

To modify profile settings, click the Ellipsis next to the profile and select "Edit" from the dropdown menu, as shown in Fig. 17.

np17-ProfileMenu
Fig. 17

The "Edit profile" dialog displays, as shown in Fig. 18.

np18-EditProfile
Fig. 18

Give this profile a more descriptive name, making it easy to identify from a list of your profiles.

By default, this profile displays only your initials. To upload your own picture, click the "Change picture" link, which displays the dialog shown in Fig. 19.

np19-ChangePhoto
Fig. 19

Click the "Add a photo" link and select a photo from your local drive. The photo will display in the Drop Zone, as shown in Fig. 20.

np20-ChangePhoto
Fig. 20

Use the controls to zoom in and out and position the photo as desired. Click the [Save] button to commit this photo.

The confirmation dialog shown in Fig. 21 displays.

np21-Profile
Fig. 21

Click the [Close] button when finished.

Deleting a Profile

If you decide you no longer need a profile, you can delete it from the Microsoft Edge Settings page. Sometimes, I create a profile for the purpose of using Single Sign On in with a customer's credentials and I no longer need this profile when my work with that customer concludes.

Open the Edge Settings page by clicking the Ellipsis in the top right corner of the browser and selecting "Settings" from the dropdown menu, as shown in Fig. 22.

np22-BrowserMenu
Fig. 22

The "Settings" page displays, as shown in Fig. 23.

np23-ProfileSettings - Copy
Fig. 23

If the "Profiles" tab is not selected, select it from the left sidebar. The current profile is highlighted, but you can view your other profiles in the "More profiles" section below. Click the [Switch] button next to one of these profiles to view the settings associated with it.

To delete a profile, click the Ellipsis next to the profile and select "Delete" from the dropdown menu, as shown in Fig. 24.

np24-ProfileMenu
Fig. 24

Conclusion

In this article, you learned how to create a new profile in Microsoft Edge, manage that profile, and delete that profile.


GCast 129:

Using the Microsoft Docker Extension for Visual Studio Code

Learn how to use the Microsoft Docker extension for Visual Studio Code to make your Docker development more efficient.


Jeff Fritz on .NET Conf

Comments [0]

Episode 715

Jeff Fritz on .NET Conf

Jeff Fritz is preparing for .NET Conf - a 3-day online conference scheduled in November, considiing with the release of .NET 7. He talks about the content of the conference, what goes into putting it on, and how you can get involved.


<< Older Posts | Newer Posts >>