Thursday, December 21, 2023

Java String Template

The first time you look at a Java String Template, it feels right and wrong simultaneously. 

String first = "John";
String last = "Smith";
String info = STR. "My first name is \{ first } and my last name is \{ last }" ;       

Right because the result of the final statement is with the placeholders correctly evaluated and filled in. It feels wrong because of the strange new "STR." expression and the way the placeholders are escaped "\{ }" 

However now that I have looked at it for some time, the reasoning does make sense and I have gotten used to the new syntax.

So let’s start with the motivation first, I will mostly follow the structure of the well-explained JEP that goes with this feature.


Motivation

It has traditionally been cumbersome to compose a string using data and expressions, taking an example from the JEP, say to compose:

10 plus 20 equals 30

would look something like this:
int x = 10;
int y = 20;
String s = new StringBuilder()
        .append(x)
        .append(" plus ")
        .append(y)
        .append(" equals ")
        .append(x + y)
        .toString();
With String Template such extensive composing is no longer needed and looks something like this:

int x = 10;
int y = 20;
String s = STR. "\{ x } plus \{ y } equals \{ x + y }" ;


Workings

So there are a few parts to it:
  1. the String template \{ x } plus \{ y } equals \{ x + y }, which is intuitive, the placeholder variables and expressions in a String template are escaped using \{ } syntax
  2. brand new expression syntax, STR.<string template>
  3. STR is the “Template Processor”, that returns the final processed string
It is illegal to have a String template without a String Template Processor, if I were to try without having it, the following is the error I get in Intellij:


String Template Processor

String Template Processor is the part that provides special powers, imagine processors that can avoid SQL injection while crafting a SQL statement!

Looking closely first at a raw String Template, itis obtained by using a RAW String Template Processor

String first = "John";
String last = "Smith";
StringTemplate st = RAW. "My first name is \{ first } and my last name is \{ last }" ;


And is composed of two sets of data:

  • fragments, which is [“My first name is” , “and my last name is” , “”] in the template above, the pieces of fixed text around the template placeholders
  • values — which are the resolved placeholders, so in my example they are [“John”, “Smith”]

A Template Processor simply uses the “fragments” and “values” to put the final output together, for eg, I can implement a custom Template Processor that capitalizes the fragments the following way:

StringTemplate.Processor<String, RuntimeException> UPPER =
        StringTemplate.Processor.of((StringTemplate st) -> {
            StringBuilder sb = new StringBuilder();
            Iterator<String> fragIter = st.fragments().iterator();
            for (Object value : st.values()) {
            sb.append(fragIter.next().toUpperCase());
            sb.append(value);
            }
            sb.append(fragIter.next());
            return sb.toString();
        });
You can imagine highly specialized String Template Processors coming up that can handle the scenario of SQL injection attacks for example.

Conclusion

String Template is a welcome addition to the Java ecosystem and simplifies scenarios where a string needs to be composed of various expressions. It is still a preview feature as of Java 21, so its use in production code is not recommended until it is GA, but it is worth checking out to see how it works.

These are the resources that go with this article:

String Template JEP — https://openjdk.org/jeps/430

Saturday, April 29, 2023

EventArc with CloudRun

 Google Cloud EventArc provides a simple way to act on events generated by a variety of Google Cloud Services.


Consider an example.

When a Cloud Build trigger is run, I want to be notified of this event -


Eventarc makes this integration simple


The internals of how it does this is documented well. Based on the source, the event is either received by EventArc directly or via Cloud Audit Logs. EventArc then dispatches the event to the destination via another pub/sub topic that it maintains. 

These underlying details are well hidden though, so as a developer concerned only about consuming the Build Events, I can focus on the payload of the event and ignore the mechanics of how EventArc gets the message from the source to my service.

Sample EventArc listener

Since I am interested in just the events and its payload, all I have to do from an application perspective is to expose an HTTP endpoint responding to a POST message with the content being the event that I am concerned about. Here is such an endpoint in Java using Spring Boot as the framework:

@RestController
public class EventArcMessageController {
    ...
    
    @RequestMapping(value = "/", method = RequestMethod.POST)
    public Mono<ResponseEntity<JsonNode>> receiveMessage(
            @RequestBody JsonNode body, @RequestHeader Map<String, String> headers) {
        LOGGER.info("Received message: {}, headers: {}", JsonUtils.writeValueAsString(body, objectMapper), headers);
        return Mono.just(ResponseEntity.ok(body));
    }
}


The full sample is available here

In this specific instance all the endpoint is doing is to log the message and the headers accompanying the message. As long as the response code is 200, EventArc would consider the handling to be successful. 

EventArc supports over 130 Google Cloud Services, so consuming myriad events from a bunch of services is easy.

EventArc Trigger

Once I have the EventArc deployed as a Cloud Run service, to integrate this with the Cloud Build Events in EventArc, all I have to do is to create an EventArc trigger. This can be done using the UI:



or using command line:

gcloud eventarc triggers update cloud-build-trigger \
--location=us-west1 \
--destination-run-service=cloudbuild-eventarc-sample \
--destination-run-region=us-west1 \
--destination-run-path="/" \
--event-filters="type=google.cloud.audit.log.v1.written" \
--event-filters="serviceName=cloudbuild.googleapis.com" \
--event-filters="methodName=google.devtools.cloudbuild.v1.CloudBuild.CreateBuild"

and that is it, EventArc handles all the underlying details of the integration.


Conclusion

I have the full java code available here which shows what a full code would look like. EventArc makes it very simple to integrate events from Google Cloud Services with custom applications.