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.


FabulousThunderbirdsThe Fabulous Thunderbirds know the blues.

Monday evening at the City Winery, they delighted the audience for two hours, moving seamlessly from jump blues to boogie-woogie to rock & roll to slow burn songs like Charles Brown's "Black Night" to funky songs like their 1980s song "Wrap It Up".

The band formed nearly five decades ago in Austin, TX. Lead vocalist Kim Wilson remains the only constant member, but the current lineup still brings strong energy and technical chops to their performance. Bob Welsh's energetic solo on "Slow Down" - a Larry Williams song made famous by The Beatles - was outstanding, as were Jerry Weldon's saxophone solos and Dean Shot's guitar solos on multiple songs.

In all those years, their only US Top-40 hit was "Tuff Enuff", which peaked at Number 10 in 1986 and which they played as the penultimate song of the evening. But the group continued to tour, and it is their live performances that drive the band and their fans. I saw them at a festival in Detroit in 1989 with Jimmie Vaughn on guitar. The names and faces have changed but the energy remains high.

The club Monday night was barely half full, but those who attended enjoyed it. A couple at the table next to mine appeared to be in their 70s or 80s but spent much of the evening dancing.

The show's opening was delayed as Weldon had trouble locating the microphone for his sax; but it closed with a strong harmonica solo by Wilson that brought the audience to its feet.

More photos


HowieDayHowie Day has a beautiful voice, is an accomplished guitar player, and has written dozens of great songs. For many, that would be enough. But Day takes it a step further in his concerts.

Thursday night at City Winery, Day appeared on stage with only a guitar and a few switches at his feet. Frequently, he would record his voice or his guitar playing or the sound of him tapping on the exterior of his instrument; then, play back these recordings on a loop to create rich layers of music. At times, he would enhance the playback with a bit of reverb. We heard musical arrangements and harmony vocals as if he were not alone on stage.

Between songs, Howie chatted casually with the audience about his life and his music. He introduced the song "Disco" by telling us he recorded it in his parents' basement "which sounds sad, but I was seventeen at the time". He sang an alternate country music version of his song "Be There", entitled "Beer There", complete with a southern twang.

Highlights included "Be There", "Tree Tops", "Longest Night", and his biggest hit "Collide". Nearly every song was an original, but he mixed in U2's "One" and opened his encore set with Crowded House's "Don't Dream It's Over".

It was a show filled with more energy than one would expect from a singer/songwriter who specializes in ballads and love songs.

I have never seen anything quite like a Howie Day performance. I have seen many musicians backed by drum machines or pre-recorded instruments and I have even seen a few record a brief snippet before playing on loop as their own accompaniment; but I have never seen a musician layer so many levels of music simultaneously and create such a rich sound as I witnessed Thursday evening.

More photos


Episode 714

Michael Mishal on Reinforcement Learning

Michael Mishal describes how reinforcement learning can use rewards to solve complex artificial intelligence problems.


July 2022 Gratitudes

Comments [0]

7/4
Today I am grateful for all the good things about the United States and for the freedom to express my opinions on the bad things about it

7/5
Today I am grateful to watch fireworks displays around the city from my balcony.

7/6
Today I am grateful for groceries delivered to my door.

7/8
Today I am grateful to sleep much of yesterday while recovering and still be able to sleep last night

7/9
Today I am grateful to the friend who gave me a bunch of really nice furniture yesterday.

7/10
Today I am grateful to those who care about my health

7/11
Today I am grateful for a call from my brother and sister-in-law in Australia yesterday.

7/12
Today I am grateful to finally test negative for COVID

7/13
Today I am grateful for:
- a kickoff to the Fiscal Year with others in my organization
- having Zoe stay with me for a couple weeks

7/14
Today I am grateful for:
-my first visit to Nebraska
-hanging out last night with the Nebraska.Code speakers

7/15
Today I am grateful to deliver a keynote presentation at the Nebraska.Code conference yesterday

7/16
Today I am grateful to Ken and the organizers and volunteers that made Nebrasa.Code a great success!

7/17
Today I am grateful to see the Nitty Gritty Dirt Band in concert last night

7/18
Today I am grateful to go bike riding this weekend for the first time since getting sick weeks ago.

7/19
Today I am grateful:
- for dinner last night with Chris and his family
- to see an entertaining David Gray concert last night

7/20
Today I am grateful:
- to attend the Microsoft Inspire event with partners at the Aon Center yesterday
- for drinks and jazz with Thad last night

7/21
Today I am grateful for an unexpected visit from my son this week

7/22
Today I am grateful for 75 years of marriage for my Uncle Bill and Aunt Jean.

7/23
Today I am grateful for my first visit to the South Loop Farmers Market at Grant Park

7/24
Today I am grateful to see the Psychedelic Furs in concert last night.

7/25
Today I am grateful to catch up on sleep yesterday and last night.

7/26
Today I am grateful to work with my trainer this morning for the first time since I became sick last month.

7/27
Today I am grateful for online training resources.

7/28
Today I am grateful for an ice cream social  at the Aon Center yesterday.

7/29

7/30
Today I am grateful to experience Teatro Zinzanni last night in Chicago

7/31
Today I am grateful for my first visit to Second City in years.

8/1
Today I am grateful that my lingering COVID symptoms are nearly gone

8/2
Today I am grateful for a new (to me) kitchen table - the first one I have owned in over 8 years!

8/3
Today I am grateful to see the Jim Irsay collection and band at Navy Pier last night

8/4
Today I am grateful for dinner with a bunch of Microsoft folks in downtown Chicago last night.

8/5
Today I am grateful:
-to co-lead a Diversity & Inclusion workshop yesterday morning
-to attend the Windy City Smokeout with Josh yesterday afternoon
-to see Howie Day in concert last night

8/6
Today I am grateful for a return to Grand Rapids, MI for the first time in years.

8/7
Today I am grateful to speak at an excellent Beer City Code conference yesterday.


TeatroZinZanni-1I have been to circuses and concerts and restaurants and theater, and dinner theater and improv shows and drag shows.

Thursday night at the Cambria Hotel in Chicago's Theater District, I experienced Teatro ZinZanni, which combined all of these into a single evening performance.

I had heard good things about this show but an announcement that it would close in two days was the motivation I needed to buy Friday evening tickets.

At center stage was a cross-dressed oversexed giantess, who shamelessly teased and flirted with the audience, driving the show forward. He/she was funny and crass and over the top.

TeatroZinZanni-2But in between, we heard excellent singers backed by a top-rate band and jugglers and acrobats and trapeze artists. The trapeze artists were the most impressive to me.

In between these, cast members ran among the tables in elaborate costumes interacting with the audience.

In between these, we enjoyed a very good dinner.

I did not know what to expect, but the show entertained us greatly.

Teatro ZinZanni has left Chicago to begin a run on the west coast. But the theater will reopen with Cafe Zazou in September. I think I know what to expect from this show.

But who knows?


GCast 128:

Maintaining State with Docker Volumes

Docker containers are stateless by default, which means that, when one is destroyed, all data created after the container is lost. However, you can get around this limitation by attaching a volume to your container. This video shows you how to create and manage Docker volumes.


To access an Azure Key Vault secret from your code, you must register your key vault as an application.

The steps are:

  1. Create the key vault
  2. Register the application with Azure Active Directory
  3. Add a Client Secret to the App Registration
  4. Add an Access Policy to the Key Vault
  5. Set Environment Variables
  6. Write the code

Create the key vault

First, you need to create a Key Vault in which to store your secrets. For instructions on how to create a Key Vault, see this article.

Register the application with Azure Active Directory

After creating a Key Vault, register the Key Vault with Azure Active Directory.

This article shows how to do this.

For our purposes, the most important pieces of information from the Application Registration are the  Application ID, which is sometimes called the Client ID.

You can find this on the Azure Active Directory "App registrations" blade. Search for your App Registration by name, as shown in Fig. 1.

kvc01-AppRegistration
Fig. 1

Record the Display name, the Application (client) ID) and the Directory (tenant) ID. You will need these later.

Add a Client Secret to the App Registration

Next, you will need to create a Client Secret within your Application Registration.

Within the App Registration, click the [Certificates & secrets] button (Fig. 2) to open the "Certificates & secrets" blade, as shown in Fig. 3

kvc02-CertificatesAndSecretsButton
Fig. 2

kvc03-CertificatesAndSecretsBlade
Fig. 3

To create a Client Secret, select the "Client secrets" tab and click the [New client secret] button (Fig. 4) to open the "Add a client secret" dialogue, as shown in Fig. 5.

kvc04-NewClientSecretButton
Fig. 4

kvc05-AddClientSecret
Fig. 5

At the "Description" field, enter a description of the secret (e.g., for which application are we generating a secret).

At the "Expires" dropdown, select how soon this secret will expire, requiring you to generate a new one.

When you finish completing the dialogue, click the [Add] button (Fig. 6) to return to the Application Registration" page, as shown in Fig. 7.

kvc06-AddButton
Fig. 6

kvc07-ClientsAndSecretsBlade
Fig. 7

Your newly created secret will display in the list on the "Client secrets" tab. Copy and save the "Value" column. After you navigate away from this page, you will no longer be able to view the Value.

Add an Access Policy to the Key Vault

An Access Policy tells Azure which users, applications, and services have access to Azure Key Vault and what actions they can take on the information stored in Key Vault. After you have registered the application, you will need to create an Access Policy in Azure Key Vault, providing the Application Registration access to the key vault.

To add an Azure Key Vault Access Policy, navigate to the Azure Portal, log in, and open the Azure Key Vault, as shown in Fig. 2.

kvc08-AzureKeyVault
Fig. 8

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

kvc09-AccessPoliciesButton
Fig. 9

kvc10-AccessPolicyBlade
Fig. 10

Click the [Add Access Policy] button (Fig. 5) to display the "Add access policy" dialogue, as shown in Fig. 6.

kvc11-AddAccessPolicyButton
Fig. 11

kvc12-AddAccessPolicyDialogue
Fig. 12

This dialogue provides a number of templates which preselect permissions to access and manage keys, secrets, and certificates in this Azure Key Vault. If you like, you can select one of these, as shown in Fig. 7.

kvc13-AccessPolicyTemplates
Fig. 13

Alternatively, you can specify each permission explicitly for keys, secrets, and certificates in this key vault. Fig. 8 shows how to select all permissions for managing secrets, which I will do for this demo.

When you have selected all the desired permissions, click the [Add] button (Fig. 8) to return to the "Add access policy" dialogue.

kvc14-SecretPermissions
Fig. 14

The next step is to give these permissions to the Application Registration. Click the link next to "Select principal" to open the "Principal" dialogue, as shown in Fig. 9.

kvc15-AddButton
Fig. 15

Search for the Application Registration by display name, select the Registration from the list, as shown in Fig. 10 and click the [Select] button to close the "Principal" dialogue and return to the "Add Access Policy" dialogue, as shown in Fig. 11.

kvc16-SelectPrincipalDialogue
Fig. 16

kvc17-AddAccessPolicy
Fig. 17

Finally, click the [Save] button (Fig. 12) to close the "Principal" dialogue and return to the "Access Policies" blade, as shown in Fig. 13. You will lose your changes if you fail to click the [Save] button before navigating

kvc18-SaveButton
Fig. 18

kvc19-AccessPoliciesBlade
Fig. 19

Set Environment Variables

The sample application below uses the DefaultAzureCredential class to authenticate the user. This class pulls information from the following environment variables:

  • AZURE_CLIENT_ID (from App Registration "Overview" blade)
  • AZURE_CLIENT_SECRET (from App Registration "Certificates & secrets" blade, "Value" field)
  • AZURE_TENANT_ID (from App Registration "Overview" blade)

The values for each of these fields were acquired in the steps above.

Write the code

In a .NET Core application, the following NuGet packages assist you when working with Azure Key Vault.

  • Azure.Security.KeyVault.Secret
  • Azure.Identity

Create a new Console Application in Visual Studio and install the Azure.Security.KeyVault.Secrets and Azure.Identity NuGet packages.

As stated above, we can use the DefaultAzureCredential class to represent the principal used to make calls to our Azure Key Vault and the information (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and AZURE_TENANT_ID) are stored in environment variables.

We create a DefaultAzureCredential object with the following code:

var credentials = new DefaultAzureCredential();

Then, we use this object to create a SecretClient object, as in the code below.

secretClient = new SecretClient(new Uri(keyVaultUri), credentials);

The SecretClient object provides methods to access and manage our Key Vault secrets.

For instance, we can get information about all the secrets in our Key

The following code retrieves information on all secrets in the Key Vault and lists the name, value, and content type of each

Console.WriteLine("All Secrets:");
var allSecrets = secretClient.GetPropertiesOfSecrets();
foreach (var secret in allSecrets)
{
     var secretValue = secretClient.GetSecret(secret.Name);
     Console.WriteLine($"{secret.Name} | {secretValue.Value.Value} | {secretValue.Value.Properties.ContentType}");
}
Console.WriteLine();

Other SecretClient methods allow us to get, set, or delete a Secret, as shown in the following code snippets:

await secretClient.SetSecretAsync(setSecretName, setSecretValue);
var secret = secretClient.GetSecret(setSecretName);
var operation = secretClient.StartDeleteSecret(deleteSecretName);

By default, Azure Key Vault supports soft delete, meaning that a deleted object can be retrieved for a given period after deletion (90 days, by default).
To permanently delete a secret prior to this, we can issue a purge command after the soft delete has completed. We can determine when the soft delete has completed by querying the Boolean DeleteSecretOperation.HasCompleted property.

if (operation.HasCompleted)
{
     secretClient.PurgeDeletedSecret(purgeSecretName);
}

Below is the full code of a .NET Core Console application

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace testKeyVaultConsoleApp
{
     internal class Program
     {
         const string KEYVALUTNAME = "dgtestkeyvault"; // Set this to the name of your key vault
         static string keyVaultUri = $"https://{KEYVALUTNAME}.vault.azure.net";

        static SecretClient secretClient = null;

        static async Task Main(string[] args)
         {
             var credentials = new DefaultAzureCredential();
             secretClient = new SecretClient(new Uri(KEYVAULTURI), credentials);

            ListAllSecrets();

            Console.Write("Input the Secret name to set (ENTER to skip):");
             var setSecretName = Console.ReadLine();
             if (setSecretName != "")
             {
                 Console.Write("Input the Secret value to set:");
                 var setSecretValue = Console.ReadLine();

                Console.WriteLine("Setting secret...");
                 await secretClient.SetSecretAsync(setSecretName, setSecretValue);
                 Console.WriteLine($"Secret {setSecretName} set to {setSecretValue}!");

                // Set content type
                 var secret = secretClient.GetSecret(setSecretName);
                 secret.Value.Properties.ContentType = "Demo Type";
                 secretClient.UpdateSecretProperties(secret.Value.Properties);
             }

            Console.Write("Input a Secret Name to soft delete (ENTER to skip):");
             var deleteSecretName = Console.ReadLine();
             if (deleteSecretName != "")
             {
                 var operation = secretClient.StartDeleteSecret(deleteSecretName);
                 Console.Write($"Deleting secret {deleteSecretName}...");
                 while (!operation.HasCompleted)
                 {
                     Thread.Sleep(1000);
                     Console.Write($".");
                     operation.UpdateStatus();
                 }
                 Console.WriteLine();
                 Console.WriteLine($"Secret {deleteSecretName} deleted!");
             }

            Console.Write("Input a Secret Name to permanently delete (ENTER to skip):");
             var purgeSecretName = Console.ReadLine();
             if (purgeSecretName != "")
             {
                 var operation = secretClient.StartDeleteSecret(purgeSecretName);
                 Console.Write($"Soft deleting secret {purgeSecretName}...");
                 while (!operation.HasCompleted)
                 {
                     Thread.Sleep(1000);
                     Console.Write($".");
                     operation.UpdateStatus();
                 }
                 Console.WriteLine();
                 Console.WriteLine($"Secret {purgeSecretName} deleted!");
                 secretClient.PurgeDeletedSecret(purgeSecretName);
                 Console.WriteLine($"Secret {purgeSecretName} purged!");
             }

            ListAllSecrets();

            Console.WriteLine();
             Console.WriteLine("Done!");
         }

        private static void ListAllSecrets()
         {
             Console.WriteLine("All Secrets:");
             var allSecrets = secretClient.GetPropertiesOfSecrets();
             foreach (var secret in allSecrets)
             {
                 var secretValue = secretClient.GetSecret(secret.Name);
                 Console.WriteLine($"{secret.Name} | {secretValue.Value.Value} | {secretValue.Value.Properties.ContentType}");
             }
             Console.WriteLine();

        }
     }
}

You can find the code here.

NOTE: Visual Studio reads Environment Variables on launch, so it may be necessary to restart Visual Studio after you set the environment variables.

Conclusion

In this article, you learned how to create a Key Vault and manage its secrets from a .NET Console application.


Episode 713

James McKee on Application Security vs Developer Security

Security Advocate James McKee describes how we can increase cybersecurity by building security into the application development process.


"The Fires of Heaven" is Robert Jordan's fifth book in "The Wheel of Time" series. Despite a plethora of characters introduced in the first four novels, this book is very much the story of Rand al'Thor. Rand is the Dragon Reborn, the foretold reincarnation of Lews Therin Telamon - a warrior king from thousands of years ago, who is destined to lead the forces of light against those of darkness in the upcoming final battle. Rand gathers and command his armies, battles dark friends, and feels his sanity slipping away as Lews Therin's thought intrude upon his mind.

Jordan is often criticized (sometimes by me) for the slow pace of the series. But the passage of time makes Rand's gradual transformation from naive shepherd to warrior king more plausible.

Two other characters grow considerably in this volume: Mat tries to run from his responsibilities but repeatedly rises to the challenges set before him; and Morgaine sacrifices her pride and more to do what is right.

Other significant developments in TFOH:

  • Villainess Lanfear's obsession with Rand and Lews Theron finally erupts into a violent confrontation
  • An ancient warrior woman is released to the real world after centuries trapped in a dream world

Perrin is notably missing from this book - presumably resting from his heroic actions in the previous novel and celebrating his honeymoon.

The action accelerates near the end of this book: major characters are killed, and others seek vengeance against their murderers. The switching perspectives in the final chapters show the same action from different points of view, giving a frantic pace to the narration.

"The Fires of Heaven" starts slowly but is saved by a strong finish.


Psychedelic FursSome things stay the same and sometimes that is a very good thing. Brothers Richard and Tim Butler formed The Psychedelic Furs in the late 1970s and they form the core of the band today. Saxophonist Mars Williams joined in 1983 and remains with the band.

Saturday night, they were joined by Amanda Kramer (keyboards), Rich Good (guitar), and Zack Alford (drums) to the delight of thousands of fans at the Aragon Ballroom in Chicago's Uptown neighborhood.

The evening began with a set by LA-based band X, a group I remember from my college days in the early 1980s. Back then, they were primarily a punk band, but they showed greater range on this night than I remember from their LPs in my dorm room. In addition to their earlier hardcore music, we heard a mix of rockabilly and alternative rock. X maintained even more consistency over the decades than the Furs. Their founding members - D. J. Bonebrake, Exene Cervenka, John Doe, and Billy Zoom still perform together and still know how to rock hard.

XListening to X primed the audience for The Psychedelic Furs, who opened by launching into the frantic "Mr. Jones", which got the crowd bouncing. Of course, the biggest cheers came when they played their biggest hits, such as "Pretty in Pink", "Love My Way", "Heaven", and "Heartbreak Beat". These songs were recorded and released in the 1980s, but they sound fresh today. Vocalist Richard Butler's voice remains unchanged over the decades and the rest of the band retains a high energy when playing these songs for the thousandth time. Thanks in large part to Richard’s vocals, the live performance retains the technical quality of their recording sessions.

The Psychedelic Furs were part of a strong group of British synthpop bands that emerged after the punk movement of the 1970s. They have had more staying power than most of their peers, thanks to strong melodies and arrangements and a commitment to touring for the past decades.

We nearly saw a second show when an unstable patron tripped and fell, knocking heads with a woman in the front row of the balcony, nearly sending both of them over the railing. Thankfully, no one was seriously injured.

And no one went home disappointed from this excellent, high-energy show.


<< Older Posts