Pokazywanie postów oznaczonych etykietą java. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą java. Pokaż wszystkie posty

środa, 26 kwietnia 2023

OpenTelemetry - what is it & why should you care?

Per project's about page (https://opentelemetry.io/about/):


OpenTelemetry provides the libraries, agents, and other components that you need to capture telemetry from your services so that you can better observe, manage, and debug them

But what does that actually mean?


Let's first take a look at the domain - telemetry. It's composed of logs, metrics and traces. First two have been present in (enterprise) Java for a long time. The third component, traces, started to gain popularity in recent years with the rise of distributed architectures.

In case of monolithic architectures, logs usually contain all necessary data, ie complete business operation. In microservices worlds it's not that simple. Engineers need to have a way to somehow correlate execution happening in multiple places, often in an asynchronous manner (messaging systems anyone?). This is where distributed tracing comes into play. Each instrumented (more on this later) component involved in processing of a business operation generates a single data point (aka span). Instrumentation also makes sure that all spans of a business operation (called trace) are correlated (with traceId) and are in a chronological order

Why is OpenTelemetry so important?


Multiple companies have been using a kind of telemetry (sometimes even distributed traces!) for a long time now. Why should they switch to OpenTelemetry now? The answer is simple - standardisation across industry leaders. I've seen multiple in-house attempts to telemetry, usually as a way to help developers hunt and fix issues - a process that is crucial for revenue streams ;-) Such solutions are often very limited due to custom protocols, lack of resources to keep them in a good shape (hackathon / side project) or limited scope / functionality. Moreover, switching telemetry backend, used to visualise and explore telemetry, is almost impossible. That's why industry leaders (Splunk, NewRelic, Microsoft, Amazon - and others) have decided to join forces and create a standard. Everything open sourced, under Cloud Native Computing Foundation (CNCF, https://www.cncf.io/). 

OK, I get the idea, but WHY should I use it?


Here are some compelling reasons:

Debugging made easier

When an issue arises in a distributed system, it can be challenging to pinpoint the root cause. With distributed tracing, you can get a holistic view of the entire request flow across different services and identify the problematic component quickly. You can trace the request path and see the exact timing, order and success/error state of each operation across different services. This can significantly reduce the time spent on debugging and resolving issues.

Performance optimization

Telemetry allows you to capture performance metrics and analyze them to optimize your system's performance. Using traces, you can identify the slowest components in the request path and optimize them to reduce latency .

Understanding system behavior

Tracing provides insights into how your distributed system behaves in production. You can observe the actual request flow, identify any bottlenecks or anomalies, and gain a better understanding of how different components interact with each other. This can help you make informed decisions about system design, resource allocation, and capacity planning. Not an easy thing in a complex distributed architecture!

System monitoring

Telemetry is a valuable tool for monitoring the health and performance of your Java applications in production. You can set up alerts and notifications based on trace data to proactively detect and resolve any issues before they impact your users. Tracing data can also be used for generating reports, dashboards, and visualizations to gain real-time insights into the state of your system.

Vendor-agnostic instrumentation

OpenTelemetry is supported by many backend vendors, including Splunk, Datadog, New Relic, and others. This means that you can easily switch between different backends for visualizing and analyzing your telemetry data without having to change your instrumentation code. This flexibility allows you to choose the backend that best fits your requirements and budget, without being locked into a specific vendor.

OK, fine, but HOW can I use it?


OpenTelemetry project provides a bunch of different components, but since this blog is mainly about Java, let's focus on.. Java ;-) In the first paragraph an "instrumentation" was mentioned. It is, in short, a way to add tracing capabilities to your Java applications without much hassle. OpenTelemetry provides libraries and an JVM agent that can be easily integrated into any codebase to automatically capture and propagate trace information.
Adding OTEL to existing deployment is as easy as adding JVM agent and few configuration properties. Latest quick start guide is always available here: https://github.com/open-telemetry/opentelemetry-java-instrumentation#getting-started

Perfect! Now WHAT can I expect as a telemetry data?


In a microservices architecture, a single business operation often spans multiple services. A typical trace would contain multiple spans, each representing a different operation in the overall business process. Each span would include a unique identifier (the spanId), a reference to its parent span (the parentSpanId) and trace identifier (traceId). This allows all the spans to be correlated and reconstructed into the full trace.

Here's an example trace with three spans:

spans: - Span: name: "Frontend service" spanId: 0123456789abcdef traceId: 098k0k0 parentSpanId: null - Span: name: "Backend service" spanId: 23456789abcdef01 traceId: 098k0k0 parentSpanId: 0123456789abcdef - Span: name: "Database service" spanId: 3456789abcdef012 traceId: 098k0k0 parentSpanId: 23456789abcdef01


Postface

I hope that all of above convinced you, my dear reader, to go and try OpenTelemetry. Please bear in mind that the project delivers much, much more that I have described here - ranging from a great number of automatically instrumented libraries to passing seamless context between various provided services (like AWS SQS for example ;-) ).

piątek, 24 marca 2023

Eliminate dreadful NPEs with NullAway

What is NullAway

Static code analysis has always been a little controversial in the Java development community. As with almost each and every tool, it can be used for good (to improve code quality) or bad (infuriate engineers with picky rules). 

Null Away (https://github.com/uber/NullAway) is however different. There are no rules to configure, no code styles to discuss. Just one plugin that will help eliminate NPEs in your code by reviewing all code execution paths.


Configuration


For Gradle

https://github.com/uber/NullAway#gradle


For Maven: 



<plugin>

        <groupId>org.apache.maven.plugins</groupId>

        <artifactId>maven-compiler-plugin</artifactId>

        <configuration>

          <source>${maven.compiler.source}</source>

          <target>${maven.compiler.target}</target>

          <fork>true</fork>

          <compilerArgs>

            <arg>-XDcompilePolicy=simple</arg>

            <arg>-Xplugin:ErrorProne -XepAllErrorsAsWarnings -XepExcludedPaths:.*/src/test/java/.* -Xep:NullAway:ERROR -XepOpt:NullAway:TreatGeneratedAsUnannotated=true -XepOpt:NullAway:ExcludedClasses=sf.cloudmetricsyncer.CloudProvider -XepOpt:NullAway:AnnotatedPackages=sf.cloudmetricsyncer,sf.externalmonitor</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>

            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>

            <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>

            <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>

          </compilerArgs>

          <annotationProcessorPaths>

            <path>

              <groupId>com.google.errorprone</groupId>

              <artifactId>error_prone_core</artifactId>

              <version>2.16</version>

            </path>

            <path>

              <groupId>com.uber.nullaway</groupId>

              <artifactId>nullaway</artifactId>

              <version>0.10.5</version>

            </path>

          </annotationProcessorPaths>

        </configuration>

      </plugin>


 

How to


For a new project it’s easy - just enable the plugin and fix the build each time it is required. For old code, especially with a large codebase, it might be trickier. NullAway plugin is not reporting all of the violations on the first run - only a subset, making the whole iterative process longer and more painful. 


Here’s a proposed approach I’ve successfully applied to a number of projects:


  1. Start with service boundaries - API, DB data model. Review which fields definitely need to be nullable. If possible implement appropriate validation to reject illegal NULLs or apply defaults.

  2. If you have a good modularization of the code - split work by sub-package, using configuration options “-XepOpt:NullAway:AnnotatedPackages=XYZ” and “-XepOpt:NullAway:UnannotatedSubPackages=ABC”

  3. Run the build and review  “returning @Nullable expression from method with @NonNull return type” messages. Consider if you really need to return NULLs in such cases. Perhaps a default value would be better.

  4. Review “initializer method does not guarantee @NonNull field XXX is initialized along all control-flow paths (remember to check for exceptions or early returns).” messages. Perhaps an instance of the class is initialized in a different way (guaranteed to be called after construction but before the first use)? If so, add a pre-configured (plugin configuration) initialization annotation to the method. This is also a great moment to think about making the data model (or parts of the model) immutable without any NULLs.

  5. Review messages pertaining the class hierarchy (super / subclass) 

    1. parameter X is @NonNull, but parameter in superclass method ABC#abc) is @Nullable"

    2. method returns @Nullable, but superclass method ABC#abc returns @NonNull

  6. Review “passing @Nullable parameter 'null' where @NonNull is required” - do you need to pass an explicit null? Perhaps a default (empty) value would be better?

  7. Review “passing @Nullable parameter XXX where @NonNull is required”. This often means that a data model class properties are nullable. Perhaps the model entity can be turned into immutable one, getting rid of NULLs there entirely?




Tips


Initialisation annotation

In some cases instance is initialized before the first use in some other way than via a constructor. This may happen in a typical framework implementing some kind of an instance lifecycle. In order to notify NullAway that fields initialization will happen in such method, one can use the initialization annotation:  https://github.com/uber/NullAway/wiki/Supported-Annotations#initialization 


NullSafeMap

Map get() is treated as inherently unsafe (nullable). In many cases however a programmer knows that map will contain mapping for a particular key. In order to use NullAway efficiently in such cases it’s a good idea to have an utility (eg NullSafeMap) with static nullSafeGet(Map, key) method annotated with @SuppressWarnings("NullAway") 


Use standards 

Out of possible annotations, I recommend: javax.annotation.Nullable. Keep it… standardized ;-)


Local variables

It’s not possible to suppress a check for a local variable - in such cases it’s best to extract a separate method (one liner) out of expression where the variable is used and annotate the method.


Generated code

One can easily ignore generated code (since they can’t do much about it) using: -XepOpt:NullAway:TreatGeneratedAsUnannotated=true 


Last resort - ignoring particular classes

Exclude some classes (only if really needed) using:  -XepOpt:NullAway:ExcludedClasses=XYZ



Now go out there, have fun coding and hunt down some nasty NullPointerExceptions! ;-)


czwartek, 12 sierpnia 2021

Snyk JVM Ecosystem Report 2021

...has just been released: https://snyk.io/jvm-ecosystem-report-2021 and gives quite interesting overview of the Java world we're living in. Full version (33 pages!) is free and available for download under this link .

Key takeaways

  1. AdoptOpenJDK dominates - 44% of developers uses this distro. Interesting point - Amazon Corretto gets only 9% share. Sounds as if the community learnt not to trust corporations ;-)
  2. Systems move away from Java 8 (more use 11 in production already). Also, more than 25% of the Java developers use Java 15 in development. That's a good thing showing that ecosystem is healthy and alive.
  3. Kotlin is the second most important language on the JVM (more popular than Groovy or Scala). There are also 531k repos in Kotlin on Github as compared to 194k in Scala / 64k in Groovy. One possible explanation is that Kotlin simply fixes what's broken in Java without overcomplicating things (yes Scala I'm looking at you). 

Most surprising (or are they?)

  1. Maven is still the most popular build system for Java with 76% of developers using it. There is more - Maven scored better numbers than a year ago! Given high popularity of Gradle here-and-there (for example in OSS projects) and general tendency to scrap XML-based tools, this is quite interesting piece of data.
  2. Domination of Spring, with more than 50% of developers using Spring Boot and almost 30% using Spring MVC. This sounds surprising as I've recently heard a lot (and I mean it - a lot) of complains about how bloated and complex Spring has become. With so many configuration modules being loaded automatically by default it's sometimes crazy hard to understand what the hell is happening when the app starts. On the other hand Spring Boot allows for startup of a new project in a mere second (or few more). Does that mean that in general developers prefer (or are required to prefer) speed of development versus quality/speed/control? 

Happy reading! Let me know if you find something surprising / interesting in the report.

piątek, 26 stycznia 2018

Type safe Java DTOs in Angular with Maven

Introduction

New Angular, as opposed to "old" Angular.JS, uses TypeScript (https://www.typescriptlang.org/). Among others, TS usage allows for a static type checking, a language feature that can prove very useful on the backend / frontend border (namely - REST API level). With a TypeScript interface / class definition for each and every DTO (Java class sent thru API), errors in usage are easily discovered at the compilation stage, well before the code reaches testing.

But how to get TS definitions for all DTOs easily, automatically, without boring process of converting Java to TypeScript? Well, if your build uses good old Maven, your're lucky ;-) There is a plugin that properly configured will output .d.ts file for all of the DTOs. Let me show you how to configure the plugin for a simple Angular application.

Maven configuration

First, we need to configure the plugin in the pom.xml of your application. Fragment may look as follows:
It simply binds the plugin's generate goal to process-classes phase, configuring by the way that:
  • Java enumerations will be mapped to TypeScript string enum (requires TypeScript 2.4)
  • plugin will consider all classes conforming to com.example.**.**Dto
  • namespace in which all of the types will be stored is yournamespace
  • output will be stored in the src/main/webapp/app/dto-definitions.d.ts file (standard Maven web app layout)
  • the output will be global (in a declared namespace, not separate module)

Angular usage

OK, so we have a .ts.d file generated, how to use it now in the Angular application? First, you need to make TypeScript compiler aware of the typings. This differs between frontend builds. In my example, I use grunt with grunt-ts plugin for TypeScript compilation. Therefore, example looks as follows:
Now, once TS compiler is aware of the namespace and contained types (interfaces actually as per configuration), rest is simple. any type can be imported in a following way:
import MyClassDto = yournamespace.MyClassDto;

Tips and tricks

Duplicated and nested Java classes

The example is really minimal and simplified. There are a lot of corner cases that will require additional configuration (or black magic ;-) ) to resolve. My favourite ones are nested classes and duplicated class names.
The first one is problematic as "$" can't be part of the class name in the TypeScript - so Outer$Nester as per Java standard won't work. What can we do? Well, my solution is to rename the class to take simple form of OuterNested.
The second one is problematic as we store all classes in one module - so duplicates from different Java packages will land in the same module - and that's a name clash. What to do then? Well, I simply join duplicated class name with last part of the package declaration. Maybe it doesn't look perfect, but in limited number of duplicated cases is just works ;-)
But how to enable such conversions for the plugin? Let's just configure a customTypeNamingFunction that transforms the name as it comes from Java into TypeScript name. Simple example may look as follows:

Fully-qualified TypeScript names

What about configuring the plugin to output Java types as fully qualified TypeScript types? After all, namespace is similar to package in this regard. Well, don't do that. Why? Because of the type ordering. Plugin and compiler will understand classes in a single namespace - that is order classes referencing other classes. But this does not work for separate packages. If you have (and you certainly do) a Java class that references another Java class in other package, the plugin may output namespace with referenced classes later than the namespace with class referring. As a result, TypeScript compilation will fail as the required type will not be found. Sorry, TS ain't Java ;-) 

czwartek, 18 stycznia 2018

Notes on password handling in Java

The title is probably a bit misleading. No there will be nothing about how to securely hash and store a password, salt etc. Instead, I wanted to emphasize one little thing that is often missed when implementing password handling, that is which data structure should be used for this purpose.

The obvious choice is of course String. This happens however not to be the best one, as char array will serve the same purpose better. Why? Because of the security. 
Java String is immutable. Therefore once created, can't be modified (cleared). There is of course a way to bypass immutability using reflection, but it should never be recommended as String is JVM class and internal (private) implementation can be changed anytime. Even once the reference is abandoned, there is no way to make GC run or actually erase the data (underlying char[]). Well-known Runtime::gc() method is only a request, not a command to JVM. What is more - it's possible that a garbage collection will mark location as empty, without actually erasing the data. On the other hand, char[] can be simply zeroed.

Above is especially important nowadays, once meltdown and spectre attacks have been uncovered. If there is a chance that a malicious process can read the data kept on heap, real programmers are enforced to minimise the risk and shrink window of opportunity for such attacks.

So, how this should be done? Well, I'd personally implement a small class (eg Password) that keeps the data as char[] and provides a dispose method, zeroing internal structure once it's no longer needed. Of course the class should be used right from the API level, so that there is no intermediate String instance keeping the password before it's copied into Password. The only issue is that caller of this class needs to remember to call the method and dispose the password data explicitely. Unfortunately as we all know, Object::finalize() method can't be relied upon, as there is completely no guarantee that such method will be ever called  (and even if it does - only by GC!). 

wtorek, 16 stycznia 2018

Hibernate performance tips & tricks

Thanks to twitter I've recently found a very nice and comprehensive list of Hibernate performance tips & tricks, written by Vlad Mihalcea. It's available on his blog here: https://vladmihalcea.com/14-high-performance-java-persistence-tips/
Really recommend it! Even with years of experience with Hibernate as a persistence provider I managed to find something new :-) 

piątek, 17 listopada 2017

JEP 286 - a case of language flexibility

Introduction


As a part of the future Java 10 specification, Oracle has proposed JEP 286 - Local Variable Type Inference. Full specification can be found here: http://openjdk.java.net/jeps/286 Since publication, it has raised a great deal of controversy among Java devs around the world. What is this all about then?

Local variable type - VAR


The proposal currently boils down to introduction of a new 'reserved type name' - var - that could be used instead of a variable type. Actual type would be then inferred by the compiler. Therefore you would be able to write:
var list = new ArrayList();
instead of:
List list = new ArrayList(); 

It should remind you of a Java 7 feature called diamond operator which was designed to eliminate verbosity of a RHS (right side assignment). Since then, Java compiler is able to infer generic construction type by looking at the variable type, for example:
List list = new ArrayList<>();
Var on the other hand aims to eliminate verbosity on the LHS (left hand assignment). Also, due to the fact that var would not be a keyword, the change is totally backward compatible, making following compilable:
var var = "var"; // ;-) 

The good, the bad (and the ugly)


So, is this good or bad? The answer is the usual one - it depends :-D

There are obvious advantages on var. The biggest one is already mentioned decrease of (unnecessary) verbosity. Does is make sense to write: 
int counter = 10 or MyOwnService service = new MyOwnService() ? 
Of course it does not, as type is apparent for writer and will be for consecutive readers of the code. However, it is easy to realize that the same feature is an obvious disadvantage in some contexts, like: 
var service = MyServiceFactory.forUserData(data)
 or even worse (as nothing is implied by context):
var result = service.calculate();
The fact that with var a result of an operation will not have an apparent type (for devloper that is) can cause even more trouble, especially in a context when result is later used in a generic way (like string conversion). Just take a look at the following, compilable code:
var ids = users.stream().map(user -> user.getId()); log.info("User ids: {}", ids);
The result in runtime will be completely unexpected, as instead of user ID list, stream's object reference will be logged. Why? Because the developer forgot (or didn't know) that the stream needs to be collected to list first. If the code had explicit typing like:
List ids = users.stream().map(user -> user.getId());
the code would simply not compile (as the compiler would know that the type expected by the developer was different than RHS). I often refactor, often work with legacy code and believe that the example is not an artificial one.

Also, taking into account the diamond operator, with var the developer will need to decide which "sugar" should be used in a particular context, either:
var list = new ArrayList();
or
List = new ArrayList<>(); 

Make peace, not VAR ?


To sum up, var has some benefits but also brings some dangers. From my perspective it's a typical case of added language flexibility, that can make life a bit easier but on the other hand can cause harm if used improperly or without caution (aka a monkey with a razor). Similar concept has been introduced into C# and developers can live with that. On the other hand, Microsoft in the official code convention guide (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) explicitely warns developers not to use var in a number of contexts. 

Taking into account a typical life of a regular developer, that is often chased by deadlines, get distracted while writing the code or simply has not enough coffee ;-) I believe that var should be limited to cases when a type is apparent (local variable and constructor call for example). As the code is read more than written, it's better to have a code that is easier to understand than shorter to write. 

piątek, 3 listopada 2017

Spring framework 5 - new & noteworthy

Spring platform


Seems that nowadays everybody at least know (and most use) Spring framework. Well, no wonder. Years ago Spring pioneered lightweight DI container for masses. Later on provided nice (and also lightweight) MVC implementation that could easily play along multiple View (UI) technologies (JSP, vanilla JS, Portlets? you name it). With each and every modern (sometimes only trendy) solution / approach they were ready (REST, polyglot persistence etc.). Apart from the main Spring Framework, there are also additional Spring projects - for example fantastic microservice-enabling Spring Boot.

Spring framework 5


Current newest and shiniest version (actually 5.0.1, reference available here) has been the first major release since December 2013. This means 2,5 years of development, quite a lot I'd say in modern fast-changing software development world. So, what are the most interesting parts of the release?

Reactive Programming


Biggest star of the show - Spring-native reactive programming (go ahead and read Reactive Manifesto if you're not familiar with whole "reactive" hype). Based upon industry-agreed specification which is called Reactive Streams (specification here). Current Java 9 implements Reactive Streams natively, but as Spring 5 is coupled with Java 8, it uses an external implementation of the API - Project Reactor (click here for more info).
The reactive module is called WebFlux. Nevertheless, Spring MVC (REST-style) is still of course supported. Nice comparison in 5 use cases of "new fancy" versus "old proven" can be found here: https://www.infoq.com/presentations/servlet-reactive-stack
In order to make an application fully reactive, it has to use non-blocking model thru all layers, down to persistence. This means that currently it will work only with Mongo, Redis etc. Unfortunately, JPA / JBDC are still inherently blocking :( This can be worked around by separating persistence layer with a queue / working thread, reading / publishing persistence events (Event Sourcing anyone? :-P ).

Core and container changes


Apart from WebFlux, 5.x brings some core enhancements. Among most notable ones is utilisation of Java 8 features. For example, core Spring interfaces have now default method implementations. It means that in many cases you won't need to extends "adapters" implementing the interfaces but simply use them directly. There are also multiple lambdas and streams all around. For example, one can now register beans using a lambda (aka "supplier").  Last but not least, reflection enhancements (for details see here: https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/enhancements.html) are now used for better/faster parameters access.
There is also a nice enhancement for large application start-up. Instead of a time-consuming classpath scanning, applications can now provide an index of candidates in the form of META-INF/spring.components file. Of course the file should be created by a plugin during build (not manually for God sake! :-) ). This should bring noticeable benefits for applications with more than 200 classes. According to the source (JIRA: https://jira.spring.io/browse/SPR-11890), 200 components / 5000 irrelevant classes starts 10% faster. Not that bad at all.

Others

  • Junit 5 fully supported, also on the "integration" side of testing
  • guava (among others) support discontinued :-( 
  • Java 8  / Java EE 7 as a minimum requirement

Further reading

Summary


To sum up, Reactive programming seems to be the main theme of the release. Rest of the updates are rather "sugar" like. It's not bad though, as we should remember to always "leave well alone" :-)