Java 8 – Streams Cookbook

Summary Of Streams

Streams in Java 8 provide a declarative approach to Collections. The simplest analogy is the your Collection is a water butt, and when you turn the tap(faucet) you create a stream which you can then process.

The advantages of Streams are –

  • Declarative
  • Parallelisable
  • Reduced Boilerplate – internal iteration

The Stream operations are either –

  • Intermediate – return streams
  • Terminal – produce result

The final point is that streams can only be traversed once

Cookbook

import java.time.Duration;
import java.util.*;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.*;
public class Winner {
    private int year;
    private String nationality;
    private String name;
    private String team;
    private int lengthKm;
    private Duration winningTime;
    private int stageWins;
    private int daysInYellow;
    
    public Winner(int year, String nationality, String name, String team, int lengthKm, Duration winningTime, int daysInYellow) {
        this.year = year;
        this.nationality = nationality;
        this.name = name;
        this.team = team;
        this.lengthKm = lengthKm;
        this.winningTime = winningTime;
        this.daysInYellow = daysInYellow;
    }
    public static final List<Winner> tdfWinners = Arrays.asList(
            new Winner(2006, "Spain", "Óscar Pereiro", "Caisse d'Epargne–Illes Balears", 3657, Duration.parse("PT89H40M27S"), 8),
            new Winner(2007, "Spain", "Alberto Contador", "Discovery Channel", 3570, Duration.parse("PT91H00M26S"), 4),
            new Winner(2008, "Spain", "Carlos Sastre", "Team CSC", 3559, Duration.parse("PT87H52M52S"), 5),
            new Winner(2009, "Spain", "Alberto Contador", "Astana", 3459, Duration.parse("PT85H48M35S"), 7),
            new Winner(2010, "Luxembourg", "Andy Schleck", "Team Saxo Bank", 3642, Duration.parse("PT91H59M27S"), 12),
            new Winner(2011, "Australia", "Cadel Evans", "BMC Racing Team", 3430, Duration.parse("PT86H12M22S"), 2),
            new Winner(2012, "Great Britain", "Bradley Wiggins", "Team Sky", 3496, Duration.parse("PT87H34M47S"), 14),
            new Winner(2013, "Great Britain", "Chris Froome", "Team Sky", 3404, Duration.parse("PT83H56M20S"), 14),
            new Winner(2014, "Italy", "Vincenzo Nibali", "Astana", 3661, Duration.parse("PT89H59M06S"), 19),
            new Winner(2015, "Great Britain", "Chris Froome", "Team Sky", 3360, Duration.parse("PT84H46M14S"), 16),
            new Winner(2016, "Great Britain", "Chris Froome", "Team Sky", 3529, Duration.parse("PT89H04M48S"), 14 ));
    public static void main(String args[]) {
        // Filter and Map -
        List<String> winnersOfToursLessThan3500km = tdfWinners
                                                        .stream()
                                                        .filter(d -> d.getLengthKm() < 3500) // Separate out Tours less than 3500km
                                                        .map(Winner::getName) // Get names of winners
                                                        .collect(toList()); // Return a list
        // Winners of Tours Less than 3500km - [Alberto Contador, Cadel Evans, Bradley Wiggins, Chris Froome, Chris Froome]        
        System.out.println("Winners of Tours Less than 3500km - " + winnersOfToursLessThan3500km);
        List<String> winnersOfToursGreaterThan3500km = tdfWinners
                                                         .stream()
                                                         .filter(d -> d.getLengthKm() >= 3500)
                                                         .map(Winner::getName)
                                                         .collect(toList());
        // Winners of Tours Greater than 3500km - [Óscar Pereiro, Alberto Contador, Carlos Sastre, Andy Schleck, Vincenzo Nibali, Chris Froome]
        System.out.println("Winners of Tours Greater than 3500km - " + winnersOfToursGreaterThan3500km);
        
        // limit -
        List<Winner> winnerObjectsOfToursLessThan3500kmLimit2 = tdfWinners
                                                                  .stream()
                                                                  .filter(d -> d.getLengthKm() < 3500)
                                                                  .limit(2)
                                                                  .collect(toList());
        // winnerObjectsOfToursLessThan3500kmLimit2 [Alberto Contador, Cadel Evans]
        System.out.println("winnerObjectsOfToursLessThan3500kmLimit2 " + winnerObjectsOfToursLessThan3500kmLimit2);
        
        List<String> firstTwoWinnersOfToursLessThan3500km = tdfWinners
                                                              .stream()
                                                              .filter(d -> d.getLengthKm() < 3500)
                                                              .map(Winner::getName)
                                                              .limit(2)
                                                              .collect(toList());
        // firstTwoWinnersOfToursLessThan3500km - [Alberto Contador, Cadel Evans]
        System.out.println("firstTwoWinnersOfToursLessThan3500km - " + firstTwoWinnersOfToursLessThan3500km);
        // filter by distinct
        List<String> distinctTDFWinners = tdfWinners
                                             .stream()
                                             .map(Winner::getName)
                                             .distinct()
                                             .collect(toList());
        System.out.println("distinctTDFWinners - " + distinctTDFWinners);
        
        
        long numberOfDistinceWinners = tdfWinners
                                          .stream()
                                          .map(Winner::getName)
                                          .distinct()
                                          .count();
        // numberOfDistinceWinners - 8
        System.out.println("numberOfDistinceWinners - " + numberOfDistinceWinners);
        
        // skip records
        List<Winner> skipEveryOtherTDFWinner = tdfWinners
                                                 .stream()
                                                 .skip(2)
                                                 .collect(toList());
        // skipEveryOtherTDFWinner - [Carlos Sastre, Alberto Contador, Andy Schleck, Cadel Evans, Bradley Wiggins, Chris Froome, Vincenzo Nibali, Chris Froome, Chris Froome]
        System.out.println("skipEveryOtherTDFWinner - " + skipEveryOtherTDFWinner);
        
        List<String> mapWinnerYearNamesToList = tdfWinners
                                                   .stream()
                                                   .map(w -> w.getYear() + " - " + w.getName())
                                                   .collect(toList());
        // mapWinnerYearNamesToList [2006 - Óscar Pereiro, 2007 - Alberto Contador, 2008 - Carlos Sastre, 2009 - Alberto Contador, 2010 - Andy Schleck, 2011 - Cadel Evans, 2012 - Bradley Wiggins, 2013 - Chris Froome, 2014 - Vincenzo Nibali, 2015 - Chris Froome, 2016 - Chris Froome]
        System.out.println("mapWinnerYearNamesToList " + mapWinnerYearNamesToList);
        
        List<Integer> mapWinnerNameLengthToList = tdfWinners
                                                    .stream()
                                                    .map(Winner::getName)
                                                    .map(String::length)
                                                    .collect(toList());
        // mapWinnerNameLengthToList [13, 16, 13, 16, 12, 11, 15, 12, 15, 12, 12]
        System.out.println("mapWinnerNameLengthToList " + mapWinnerNameLengthToList);
        
        
        // matching - allMatch, noneMatch
        Optional<Winner> winner2012 = tdfWinners.stream().filter(w -> w.getName().contains("Wiggins")).findAny();
        // winner2012 - Bradley Wiggins
        System.out.println("winner2012 - " + winner2012.get());
        
        Optional<Integer> winnerYear2014 = tdfWinners.stream().map(Winner::getYear).filter(x -> x == 2014).findFirst();
        // winnerYear2014 - 2014
        System.out.println("winnerYear2014 - " + winnerYear2014.get());
        
        
        // reducing - 0 --> initial value
        int totalDistance = tdfWinners.stream().map(Winner::getLengthKm).reduce(0, Integer::sum);
        // totalDistance - 38767
        System.out.println("totalDistance - " + totalDistance);
        
        Optional<Integer> shortestYear = tdfWinners.stream().map(Winner::getLengthKm).reduce(Integer::min);
        // shortestYear - 3360
        System.out.println("shortestYear - " + shortestYear.get());
        
        Optional<Integer> longestYear = tdfWinners.stream().map(Winner::getLengthKm).reduce(Integer::max);
        // longestYear - 3661
        System.out.println("longestYear - " + longestYear.get());
        
        Optional<Winner> fastestWinner = tdfWinners.stream().min(Comparator.comparingDouble(Winner::getAveSpeed));
        System.out.println("fastestTDF - " + fastestWinner.get());
        
        // shorthand
        OptionalDouble fastestTDF = tdfWinners.stream().mapToDouble(Winner::getAveSpeed).min();
        // fastestTDF - 39.0
        System.out.println("fastestTDF - " + fastestTDF.getAsDouble());
        
        
        // groupingby - make a map whose keys are names
        Map<String, List<Winner>> namesVsWinner = tdfWinners.stream().collect(groupingBy(Winner::getName));
        // namesVsWinner - {Bradley Wiggins=[Bradley Wiggins], Carlos Sastre=[Carlos Sastre], Cadel Evans=[Cadel Evans], Óscar Pereiro=[Óscar Pereiro], Chris Froome=[Chris Froome, Chris Froome, Chris Froome], Andy Schleck=[Andy Schleck], Alberto Contador=[Alberto Contador, Alberto Contador], Vincenzo Nibali=[Vincenzo Nibali]}
        System.out.println("namesVsWinner - " + namesVsWinner);
        // join strings
        String allTDFWinnersTeamsCSV = tdfWinners.stream().map(Winner::getTeam).collect(joining(", "));
        // allTDFWinnersTeams Caisse d'Epargne–Illes Balears, Discovery Channel, Team CSC, Astana, Team Saxo Bank, BMC Racing Team, Team Sky, Team Sky, Astana, Team Sky, Team Sky
        System.out.println("allTDFWinnersTeams " + allTDFWinnersTeamsCSV);
        // grouping
        Map<String, List<Winner>> winnersByNationality = tdfWinners.stream().collect(groupingBy(Winner::getNationality));
        // winnersByNationality - {Great Britain=[Bradley Wiggins, Chris Froome, Chris Froome, Chris Froome], Luxembourg=[Andy Schleck], Italy=[Vincenzo Nibali], Australia=[Cadel Evans], Spain=[Óscar Pereiro, Alberto Contador, Carlos Sastre, Alberto Contador]}
        System.out.println("winnersByNationality - " + winnersByNationality);
        Map<String, Long> winsByNationalityCounting = tdfWinners.stream().collect(groupingBy(Winner::getNationality, counting()));
        // winsByNationalityCounting - {Great Britain=4, Luxembourg=1, Italy=1, Australia=1, Spain=4}
        System.out.println("winsByNationalityCounting - " + winsByNationalityCounting);
        
    }
    
    public double getAveSpeed() {
        return (getLengthKm() / (getWinningTime().getSeconds() / 3600) );
    }
    
    public int getYear() {
        return year;
    }
    public void setYear(int year) {
        this.year = year;
    }
    public String getNationality() {
        return nationality;
    }
    public void setNationality(String nationality) {
        this.nationality = nationality;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getTeam() {
        return team;
    }
    public void setTeam(String team) {
        this.team = team;
    }
    public int getLengthKm() {
        return lengthKm;
    }
    public void setLengthKm(int lengthKm) {
        this.lengthKm = lengthKm;
    }
    public Duration getWinningTime() {
        return winningTime;
    }
    public void setWinningTime(Duration winningTime) {
        this.winningTime = winningTime;
    }
    
    public int getStageWins() {
        return stageWins;
    }
    public void setStageWins(int stageWins) {
        this.stageWins = stageWins;
    }
    public int getDaysInYellow() {
        return daysInYellow;
    }
    public void setDaysInYellow(int daysInYellow) {
        this.daysInYellow = daysInYellow;
    }
    @Override
    public String toString() {
        return name;
    }    
    
}

How Lambda’s And Anonymous Inner Classes(AIC) Work

At first glance a lambda looks like a short hand version of an anonymous inner class. But they are not. This post covers the differences between lambda’s and AIC’s

Key Points

  • Lambdas implement a functional interface
  • Anonymous Inner Classes can extend a class or implement an interface with any number of methods
  • Variables – Lambda’s can only access final or effectively final
  • State – Anonymous inner classes can use instance variables so can have state, lambda’s cannot
  • Scope – Lambda part of its enclosing scope – so cant define a variable with same name as variable in enclosing scope
  • Compilation – Anonymous compiles to a class, while lambda is an invokedynamic instruction

How they work

Anonymous Inner Classes(AIC’s)

  • Compiler generates a class file for each anonymous inner class
  • For example – AnonymousInnerClass$1.class
  • Like all classes it needs to be loaded and verified at startup

Lambda

The key to the lambda implementation is the InvokeDynamic instruction introduced in Java 7. This allows dynamic languages to bind to symbol at runtime.

A lambda works like this –

  • Generates invokedynamic call site, and uses a lambdafactory to return the functional implementation
  • lambda convered to a method to be invoked by invokedynamic
  • Method stored in class as private static method
  • Two lambda types – non-capturing – only uses fields inside its body
    – capturing  – accesses fields outside its body

Non-capturing Lambda

Doesnt access fields outside its body

public class NonCapturingLambda {
    public static void main(String[] args) {
        Runnable nonCapturingLambda = () -> System.out.println("NonCapturingLambda");
        nonCapturingLambda.run();
    }
}

If we decode the class file using the cfr decompiler, we see the –

  • LambdaMetafactory
  • Lambda is a static void method in our class
java -jar cfr_0_119.jar NonCapturingLambda --decodelambdas false
/*
 * Decompiled with CFR 0_119.
 */
import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;

public class NonCapturingLambda {
    public static void main(String[] args) {
        Runnable nonCapturingLambda = (Runnable)LambdaMetafactory.metafactory(null, null, null, ()V, lambda$0(), ()V)();
        nonCapturingLambda.run();
    }

    private static /* synthetic */ void lambda$0() {
        System.out.println("NonCapturingLambda");
    }
}

Capturing Lambda

Accesses final or effectively final fields outside its body –

public class CapturingLambda {
    public static void main(String[] args) {
        String effectivelyFinal = "effectivelyFinal";
        Runnable capturingLambda = () -> System.out.println("capturingLambda " + effectivelyFinal);
        capturingLambda.run();
    }
}

This decompiles to –

java -jar cfr_0_119.jar CapturingLambda --decodelambdas false
/*
 * Decompiled with CFR 0_119.
 */
import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;

public class CapturingLambda {
    public static void main(String[] args) {
        String effectivelyFinal = "effectivelyFinal";
        Runnable capturingLambda = (Runnable)LambdaMetafactory.metafactory(null, null, null, ()V, lambda$0(java.lang.String ), ()V)((String)effectivelyFinal);
        capturingLambda.run();
    }

    private static /* synthetic */ void lambda$0(String string) {
        System.out.println("capturingLambda " + string);
    }
}

The interesting part is the lambda$0 method signature has gone from empty to taking a parameter String

References

https://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood

http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html

 

 

 

 

 

Another Java Lamdba Post

The syntax of a lambda is simple –

(parameters) -> expression
(parameters) -> { statements; }

Examples

Runnable r1 = () -> System.out.println("Hello World");

// or Streams
List winnersOfToursLessThan3500km = 
                           tdfWinners
                               .stream()
                               .filter(d -> d.getLengthKm() < 3500) // Separate out Tours less than 3500km
                               .map(Winner::getName) // Get names of winners
                               .collect(toList()); // Return a list

Key points are –

  • Concise – Before Java8 we would use anonymous classes to deliver “similar” functionality
  • Anonymous – no explicit name
  • Function – not associated with a particular class
  • Argument – can be passed as argument or Stored in a variable

Functional Interfaces

The key requirement for a lambda is that the interface it implements can only have a Single Abstract Method(SAM). However they can have any number of default and static methods

@FunctionalInterface Annotation

@FunctionalInterface is Optional, and enforces this rule by generating a compiler error –

Invalid '@FunctionalInterface' annotation; NotFunctionalInteface is not a functional interface

Java8 contains a number of useful interfaces in java.util.function –

Predicate   - boolean test(T t) - True/False condition on t
Consumer    - void accept(T t)  - Accepts t but returns no result
Function<T, R> - R apply(T t)      - Takes t and returns an instance of R

Functional Descriptor

Describes the signature of the Functional Interface –

Predicate - t  -> boolean
Consumer  - t  -> void
Function  - t  -> R

Accessing Class Variables

  • final or “effectively final”(not changed after initialization)
        String finalString = "final string";
        String effectivelyFinalString = "effectively final string";
        Runnable r = () -> {
            System.out.println("Hi im " + finalString);
            System.out.println("Hi im " + effectivelyFinalString);
        };
        new Thread(r).start();

But trying to change effectivelyFinalString results stops it compiling –

       String finalString = "final string";
        String effectivelyFinalString = "effectively final string";
        Runnable r = () -> {
            System.out.println("Hi im " + finalString);
            System.out.println("Hi im " + effectivelyFinalString);
        };
        effectivelyFinalString = "now i wont compile";
        new Thread(r).start();

The reason this works is that the lambda is accessing a copy of the final or “effectively final” field.

Method References – ::

The final piece of the lambda jigsaw is Method References. Method references are simply a further shorthand in the body of the lambda – so where we have a lambda –

List winnersOfToursGreaterThan3500km = 
          tdfWinners
             .stream()
             .filter(d -> d.getLengthKm() >= 3500)
             .map(w -> w.getName())
             .collect(toList());

We see map has the lambda – w -> w.getName(). This represents this method –

 Stream map(Function<? super T, ? extends R> mapper);

A method reference lets us shorthand that too –

List winnersOfToursGreaterThan3500km = 
          tdfWinners
             .stream()
             .filter(d -> d.getLengthKm() >= 3500)
             .map(W::getName)
             .collect(toList());

As with a lot of Java8 the purpose of this shorthand is to allow us to write cleaner more concise code

There are 4 types of method reference in Java 8 –

static method ContainingClass::staticMethodName
instance method of a particular object containingObject::instanceMethodName
instance method of an arbitrary object of a particular type ContainingType::methodName
constructor ClassName::new

Google – Site Reliability Engineering

Id previously posted a review of the O’Reilly Site Reliablity Engineering book by Betsy Beyer(Editor) and Chris Jones(Editor). I consider it an important book for any developer or admin to read as it covers googles approach to deploying and scaling their systems. The good news is google have released the book online at Site Reliability Engineering

The book is structured as a series of essays, and the best approach to reading it is to choose the chapters that look like they are most useful to you, and read them. In my case the most relevent chapters were –

  • Structure of Googles Applications
  • Risk and SLA’s
  • Monitoring and Automation

The ultimate aim of an Site Reliability Engineer is that the site should be self managing, and the SRE writes the systems to achieve this.

The most important points I learnt were –

  • Monitoring and Health Checks – They recommend using prometheus.io, and build alerting on top
  • SLA – Does not have to be all encompassing – for example if you cant serve images, but can provide a text based service then you can split the SLA
  • Simplicity – The most important skill a developer can have is to keep it simple

Putting It Into Practice

From my point of view the most important thing to add to my applications was health checks and monitoring. This was easy since most modern frameworks, Spring Boot and dropwizard, already contain health check API’s.

Explaining Functional Programming

It occured to me that while I know what Functional Programming is, I would struggle to explain it. My description would be something like –

You know how Object Oriented Programming is about objects? Well Functional Programming is about functions, you know lambda's, Haskell and Scala

Mmmmnnnn…. not good.

This post is my attempt to provide a better description of functional programming, and let me know any feedback or mistakes

What is Functional Programming?

Functional Programming uses mathematical functions to solve problems. Functions take an input and give an output, without changing the input –

f(x) -> some function of x

Common Features

  • Functions are first class entities – can be assigned to variables, passed as arguments, or returned from other functions
  • No state
  • No side effects – Anything the code does except produce an output from given inputs

Consider the example below the where setting I changes the changeState field, and also log. The are side effects as they go beyond returning an output from an input.

public class SideEffectExample {
   private static final Logger LOGGER = Logger.getLogger(SideEffectExample.class.getName());
   boolean changeState = false;
   int i = 1;
   public int setI(int i) {
       this.i = i;
       this.changeState = true;
       LOGGER.info("change state");
       return i + 1;
   }
}
  • Lazy evaluation – Compiler can decided when best to run a function

The degree to which the above features are enforced depends on the language

Advantages of Functional Programming?

Functional Programming means that a function will give the same output for a given input, and doesnt alter state. This makes it well suited for problems involving –

  • concurrency
  • multi-threading
  • multi-processors

Reference

https://en.wikipedia.org/wiki/Side_effect_%28computer_science%29

Spring Data JPA – @Query – Not supported for DML operations

I was wanting to update a database table status with a list of ID’s so decided the simplest way was to use @Query and JPAQL –

public interface MyJPAObjectRepository extends BaseRepository<MyJPAObject, String> {
    @Query("update MyJPAObject m set m.status = ?1 where m.id in ?2")
    void updateMyJPAObjectStatus(Status status, List<String> idList);
}

When I ran the code I got this exception –

Caused by: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.glenware.MyJPAObject m set m.status = ?1 where m.id in (:x2_0_)]

The reason is the code is calling execute, and we need executeUpdate. The fix is fairly simple – you add the @Modifying annotation –

public interface MyJPAObjectRepository extends BaseRepository<MyJPAObject, String> {
    @Modifying
    @Query("update MyJPAObject m set m.status = ?1 where m.id in ?2")
    void updateMyJPAObjectStatus(Status status, List<String> idList);
}

The @Modifying annotaion is used to instruct spring-data that this is a “modifying-query”, and allow DML operations

Reference

http://docs.spring.io/spring-data/jpa/docs/1.10.6.RELEASE/reference/html/#jpa.modifying-queries

Why do we still create Util classes?

Its been niggling at me for a while, a few years even, but why do we insist on creating our own util classes like StringUtil’s, DateUtil’s or CollectionUtil’s when we have great open source projects that contain these classes and methods? In fact recently I foolishly created a CSVUtil class instead of using apache commons csv or opencsv

The most common method you see is a StringUtil.isNullOrEmpty. It will be similar to –

public static boolean isNullOrEmpty(String myString) {
   if (myString == null || "".equals(myString)) {
      return true;
   } else {
      return false;
   }
}

Its not bad code, and keeps you DRY (Dont Repeat Yourself) from repeated null or empty string checks.

But the problem is –

  • Reinventing the wheel because there are a lot of great libraries providing this util and more
  • Created an artifact that you need to support, write unit tests
  • Impact on code coverage – if you dont unit test then you get a black mark against your code coverage
  • Chance you have introduced a bug

One reason we see code like this is they are an organisational software legacy, where in the pre-Java8 world we checked for null as we didnt have Optional classes.

What to use instead?

  • Apache Commons – In my experience this is the most common set of utilities, and probably closely match the util they would replace
  • Google Guava – These are newer than Apache Commons, although are now well established. The approach is slightly different and allow you to method chain. This might suit your coding style better

Selection

The choice of library isnt as important as consistency – so try not to mix apache commons in one part of the code, with the guava implementation in another part of the code. This will make future maitenance easier

Caveat

As with all the best rules there are times when you need to break them. I would create a util class when –

  • Specific Implementation – Package I’m using doesnt contain the method I need, but I would try to use the existing util as the basis for this
  • pre-Java8 – I might create a util as a facade on say Date’s or because I dont have Optional
  • Procrastination – The final reason for creating the util is to procrastinate, and avoid my main task! I create the util then at least I can feel productive at my next standup 🙂

So my New Years resolution for 2017 is to stop creating these classes and use tried and tested utils, and try and replace them when I see them!

 

Best Tech Book I’ve Read This Year

I had seen Site Reliability Engineering: How Google Runs Production Systems by Betsy Beyer(Editor) and Chris Jones(Editor) discussed on tech forums for a while before I bought it. The posts were all saying how thought provoking the book was, and how it led the developers to change their approach to building applications. I could totally relate to this after reading the book.

The book is a blueprint for how google manages services, and is structured as a collection of essays covering subjects including –

  • Structure of Googles Applications
  • Risk and SLA’s
  • Monitoring and Automation

The advantage of this approach is you can focus on the chapters which are most relevant to you, so if you are supporting a 1bn users you will have different needs than someone who wants a side project to self manage itself.

Site Reliability Engineer – SRE

The basic idea of a Site Reliability Engineer is that developers develop systems that support the deployed application. Specifically creating structures that monitor, scale and self repair. This means that the application can scale itself. Obviously its not that easy, and google ceases work on Site Reliability once the SLA has been reached, and interestingly if the SLA isnt reached in a given time period then they will stop the service themselves to check for other systems relying on the service

Key Things I Learned

  • Monitoring and Health Checks – From a Java point of view I can monitor and write health check statistics and monitor these through prometheus.io, and build alerting on top
  • SLA – The SLA does not have to be all encompassing – for example if you cant serve images, but can provide a text based service then you can split the SLA
  • Simplicity – The most important thing a developer can learn is to keep it simple

I did feel that there was a lot of material for large teams, and not every workplace has the skills for a dedicated SRS team. In these circumstances it is important for each layer to accept part of the responsibility. As a developer the most important thing I can do is keep it simple, and make metrics available.

Conclusion

My advice is to get this book, and choose the principles that best suit your organisation.

 

 

Auto-Generating Spring Security : Encryption and Encoding

It is simple to add security to Spring Security using spring-security-generator. You simply select an option from the Encryption and Encoding section.

screenshot4

The default is No Encryption or Encoding, which is what the previous tutorials have been using.

The other options are –

You dont want to look beyond BCryptPasswordEncoder, but I’ve included the other options if you want to experiment.

The final step is to add some users with the appropriate encryption and encoding to import.xml - 


--
insert into custom_users (username, password, enabled) values ('customadmin', 'customadmin', true);
insert into custom_authorities (username, authority) values ('customadmin', 'ROLE_CUSTOM_ADMIN');
-- NoOpPasswordEncoder - pw - NoOpPasswordEncoder
insert into custom_users (username, password, enabled) values ('NoOpPasswordEncoder', 'NoOpPasswordEncoder', true);
insert into custom_authorities (username, authority) values ('NoOpPasswordEncoder', 'ROLE_CUSTOM_ADMIN');
-- StandardPasswordEncoder - pw - StandardPasswordEncoder
insert into custom_users (username, password, enabled) values ('StandardPasswordEncoder', 'c2ee3d2c461b7d9ea9df6e26f831eccef9418bcc5940132c019b24a484600ada6131eb688a3bc9b8', true);
insert into custom_authorities (username, authority) values ('StandardPasswordEncoder', 'ROLE_CUSTOM_ADMIN');
-- BCryptPasswordEncoder - pw - BCryptPasswordEncoder
insert into custom_users (username, password, enabled) values ('BCryptPasswordEncoder', '$2a$10$RwGDv6rq.T9DE64xH6LSgOx4RV1Xn9lf6ocafwaL6AWrza40PHaMK', true);
insert into custom_authorities (username, authority) values ('BCryptPasswordEncoder', 'ROLE_CUSTOM_ADMIN');
--

The username and passwords are the same in all cases, so BCryptPasswordEncoder has a password BCryptPasswordEncoder.

You can now test the Encryption and Encoding options by generating the code. Remember that you can enable the database console using the previous tutorial

package com.glenware.springboot;

import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
         auth
             .jdbcAuthentication()
                 .passwordEncoder(passwordEncoder())
                 .dataSource(dataSource)
                   .usersByUsernameQuery(
                   "select username, password, enabled from custom_users where username = ?")
                   .authoritiesByUsernameQuery(
                   "select username, authority from custom_authorities where username = ?");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/webjars/**","/about.html","/rest/**").permitAll()
                .antMatchers("/admin/**").hasAnyRole("CUSTOM_ADMIN")
                .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/admin/admin.html")
                    .failureUrl("/login")
                    .permitAll()
             .and()
                .logout()
                    .logoutSuccessUrl("/")
                    .permitAll()
                    ;
    }
    
}

 

 

 

Auto-Generating Spring Security: Accessing the In-memory Database

I came across a blog post on Spring Framework guru’s  which uses the h2 database console, and thought it would be useful to combine the console with my own spring security tutorials –

I’ve updated the parkrunpb project on github to replace hsqldb with h2database. Ive also introduced a new class WebConfiguration.java, which registers the h2 database servlet

Start the application –

mvn spring-boot:run

Access The Console

You can access the console through -http://localhost:8080/console

console2

You then make sure the JDBC URL is –

jdbc:h2:mem:testdb

And login –

console3

The layout shows the tables we loaded in schema.sql on the right (CUSTOM_AUTHORITIES, CUSTOM_USERS and PARKRUNCOURSE)

Combine it with spring security

The next step is to combine with Spring Security, so I’ll use the configuration from the previous tutorial –Auto-generating Spring Security Tutorial – Custom JDBC Realms

We start with our class –

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
         auth
             .jdbcAuthentication()
                 .dataSource(dataSource)
                   .usersByUsernameQuery(
                   "select username, password, enabled from custom_users where username = ?")
                   .authoritiesByUsernameQuery(
                   "select username, authority from custom_authorities where username = ?");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/webjars/**","/about.html","/rest/**").permitAll()
                .antMatchers("/admin/**").hasAnyRole("CUSTOM_ADMIN")
                .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/admin/admin.html")
                    .failureUrl("/login")
                    .permitAll()
             .and()
                .logout()
                    .logoutSuccessUrl("/")
                    .permitAll()
                    ;                    
    }    
}

We then add to the configure method –

        http.authorizeRequests().antMatchers("/").permitAll().and()
                .authorizeRequests().antMatchers("/console/**").permitAll();
         http.csrf().disable();
        http.headers().frameOptions().disable();

The method then becomes –

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/webjars/**","/about.html","/rest/**").permitAll()
                .antMatchers("/admin/**").hasAnyRole("CUSTOM_ADMIN")
                .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/admin/admin.html")
                    .failureUrl("/login")
                    .permitAll()
             .and()
                .logout()
                    .logoutSuccessUrl("/")
                    .permitAll()
                    ;
                    
        http.authorizeRequests().antMatchers("/").permitAll().and()
                .authorizeRequests().antMatchers("/console/**").permitAll();
         http.csrf().disable();
        http.headers().frameOptions().disable();
                    
    }

This means the normal security from the original tutorial is applied to the application, but we have a special rule for the console

You can then test the application as before with the username/password customadmin/customadmin. You could also insert or update courses,