@Delabassee @JosePaumard#SE84EE
Java EE devs
Java SE 8 for
@Delabassee @JosePaumard#Devoxx #SE84EE
Agenda
Java SE 8 has been released ~1.5 year ago
Java EE 7 Application Servers
- GlassFish, WildFly, Liberty Profile, WebLogic, etc.
@Delabassee @JosePaumard#Devoxx #SE84EE
Agenda
Java SE 8 has been released ~1.5 year ago
Java EE 7 Application Servers
- GlassFish, WildFly, Liberty Profile, WebLogic, etc.
Check some important Java SE 8 new features
for Java EE developers... with patterns!
@Delabassee
@JosePaumard
@JosePaumard
start
The following is intended to outline our general product direction.
It is intended for information purposes only, and may not be
incorporated into any contract. It is not a commitment to deliver
any material, code, or functionality, and should not be relied upon
in making purchasing decisions. The development, release, and
timing of any features or functionality described for Oracle’s
products remains at the sole discretion of Oracle.
@Delabassee @JosePaumard#Devoxx #SE84EE
Questions?
#SE84EE
http://slido.com
Date
@Delabassee @JosePaumard#Devoxx #SE84EE
New Date & Time API
Date & Time API is a new introduction in Java 8
Initiated by Stephen Colebourne, based on Joda Time
Complete replacement of Date & Calendar
@Delabassee @JosePaumard#Devoxx #SE84EE
Date: ZonedTime
Useful for localized times
Set<String> allZonesIds = ZoneId.getAvailableZoneIds() ;
String ukTZ = ZoneId.of("Europe/Paris") ;
@Delabassee @JosePaumard#Devoxx #SE84EE
Date: ZonedTime
Useful for localized times
System.out.println(
ZonedDateTime.of(
1998, Month.JULY.getValue(), 12, // year / month / day
22, 0, 0, 0, // h / mn / s / nanos
ZoneId.of("Europe/Paris"))
); // prints 1998-07-22T22:00-00:01:15[Europe/London]
@Delabassee @JosePaumard#Devoxx #SE84EE
Date: ZonedTime
Useful for localized times
System.out.println(
ZonedDateTime.of(
1998, Month.JULY.getValue(), 12, // year / month / day
22, 0, 0, 0, // h / mn / s / nanos
ZoneId.of("Europe/Paris"))
);
ZonedDateTime nextWorldCup =
currentWorldCup.plus(Period.ofYear(4));
@Delabassee @JosePaumard#Devoxx #SE84EE
Date: ZonedTime
Useful for localized times
System.out.println(
ZonedDateTime.of(
1998, Month.JULY.getValue(), 12, // year / month / day
22, 0, 0, 0, // h / mn / s / nanos
ZoneId.of("Europe/Paris"))
);
ZonedDateTime nextWorldCup =
currentWorldCup.plus(Period.ofYear(4));
ZonedDateTime nexWorldCupJapan=
nextWorldCup.withZoneSameInstant(ZoneId.of("Asia/Tokyo")) ;
@Delabassee @JosePaumard#Devoxx #SE84EE
Date: bridges with java.util.Date
Conversions between j.u.Date and Date & Time
Time time = Time.from(localTime); // API -> legacy
LocalTime localTime = time.toLocalTime(); // legacy -> new API
Date date = Date.from(localDate); // API -> legacy
LocalDate localDate = date.toLocalDate(); // legacy -> new API
TimeStamp time = TimeStamp.from(instant); // API -> legacy
Instant instant = time.toInstant(); // legacy -> new API
Date date = Date.from(instant); // API -> legacy
Instant instant = date.toInstant(); // legacy -> new API
@Delabassee @JosePaumard#Devoxx #SE84EE
Usable in JPA
JPA does not support this Date & Time API (yet)
But with converters, we can still use it
@Entity
public abstract class AbstractPersistent {
@Convert(converter=DateConverter.class)
private Instant instant;
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Usable in JPA
The converter is a classical object
public class DateConverter
implements AttributeConverter<Instant, Date> {
public Date convertToDatabaseColumn(Instant instant) {
return Date.from(instant);
}
public Instant convertToEntityAttribute(Date date) {
return date.toInstant();
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Usable in JPA
The converter is a classical object
public class DateFormatConverter
implements AttributeConverter<ZonedDateTime, String> {
public String convertToDatabaseColumn(ZonedDateTime time) {
return DateTimeFormatter.ISO_DATE_TIME.format(time);
}
public ZonedDateTime convertToEntityAttribute(String formated) {
return DateTimeFormatter.ISO_DATE_TIME
.parse(formated, ZonedDateTime::from);
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Usable in JPA
With JPA converters we can use the types
from Date & Time API and map in j.u.l.Date or String
@Delabassee @JosePaumard#Devoxx #SE84EE
Usable in JSF
With a Custom JSF Converter
@FacesConverter("instantConverter")
public class TimeConverter implements javax.faces.convert.Converter {
@Override
public Object getAsObject(FacesContext ctx, … , String value) {
return Instant.parse(value);
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Usable in JSF
With a Custom JSF Converter...
@Override
public String getAsString(FacesContext ctx, … , Object value) {
DateTimeFormatter formatter =
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.US)
.withZone(ZoneId.systemDefault());
return formatter.format((TemporalAccessor) value);
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Usable in JSF
With a Custom JSF Converter<h:form>
<h:inputText id = "date”
value = "#{order.timestamp}"
size = "20" required="true”
label = "start"
converter = "instantConverter" />
...
@ManagedBean(name="order") @SessionScoped
public class myBean implements Serializable{
Instant timestamp;
…
Annotations
@Delabassee @JosePaumard#Devoxx #SE84EE
Annotations in Java 7
Wrapping annotations
An annotation cannot be applied more than once
@NamedQueries({
@NamedQuery(name=SELECT_ALL, query="..."),
@NamedQuery(name=COUNT_ALL, query="...")
})
public class Customer {
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Annotations in Java 8
Java 8 makes it possible!
@NamedQuery(name=SELECT_ALL, query="..."),
@NamedQuery(name=COUNT_ALL, query="...")
public class Customer {
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Bean Validation
Suppose we want to validate a parameter
public orderdCar order( @CheckCar("VW") Car aCar ) {
...
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Bean Validation
Here is the Bean Validation code for @CheckCar
@Target({PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = CarValidator.class)
public @interface CheckCar {
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String value();
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Bean Validation
And for the validator
public class CarValidator
implements ConstraintValidator<CheckCar, Car> {
private String carBrand;
public void initialize(CheckCar constraintAnnotation) {
this.carBrand = constraintAnnotation.value();
}
public boolean isValid(Car obj, ConstraintValidatorContext ctrCtx) {
if (object == null) return true;
return (!obj.getBrand().toUpperCase().contains(carBrand));
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Bean Validation
And the Java 8 trick to make it repeatable
@Target({PARAMETER})
@Retention(RUNTIME)
@Repeatable(CheckCars.class)
@Constraint(validatedBy = CarValidator.class)
public @interface CheckCar {
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String value();
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Bean Validation
And the Java 8 trick to make it repeatable
@Target({PARAMETER})
@Retention(RUNTIME)
public @interface CheckCars {
CheckCar[] value() default{};
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Bean Validation
Suppose we want to validate a parameter
public orderdCar order( @CheckCar("VW") @ChechCar("Audi") Car aCar ) {
...
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Type annotations
Annotations can be now put on types
Example 1: tell that a variable should not be null
private @NonNull List<Person> persons = ... ;
@Delabassee @JosePaumard#Devoxx #SE84EE
Type annotations
Annotations can be now put on types
Example 1: tell that a variable should not be null
Example 2: the content should not be null neither
private @NonNull List<Person> persons = ... ;
private @NonNull List<@NonNull Person> persons = ... ;
String
@Delabassee @JosePaumard#Devoxx #SE84EE
Joining strings
New String joiners are coming to the JDK!
@Delabassee @JosePaumard#Devoxx #SE84EE
Joining strings
StringJoiner class
And it prints:
StringJoiner sj = new StringJoiner(", ") ;
sj.add("one").add("two").add("three") ;
String s = sj.toString() ;
System.out.println(s) ;
> one, two, three
@Delabassee @JosePaumard#Devoxx #SE84EE
Joining strings
StringJoiner with prefix / postfix
And it prints:
StringJoiner sj = new StringJoiner(", ", "{", "}") ;
sj.add("one").add("two").add("three") ;
String s = sj.toString() ;
System.out.println(s) ;
> {one, two, three}
@Delabassee @JosePaumard#Devoxx #SE84EE
Joining strings
Also accessible from the String class
And it prints:
// From the String class, with a vararg
String s = String.join(", ", "one", "two", "three");
System.out.println(s);
> one, two, three
@Delabassee @JosePaumard#Devoxx #SE84EE
Joining strings
And directly from a List
// From a list to a String using the Stream & Collectors API
String s =
listOfStrings.stream()
.collect(Collectors.joining(", "));
System.out.println(s);
> one, two, three
@Delabassee @JosePaumard#Devoxx #SE84EE
Joining strings
And directly from a List
The result is
// From the String class, with a vararg
String s =
listOfStrings.stream()
.collect(Collectors.joining(", ", "{", "}"));
System.out.println(s);
> {one, two, three}
@Delabassee @JosePaumard#Devoxx #SE84EE
Support for Base64 decoding
After a few years…
import java.util.Base64;
String txt = "Modern Times!";
String encoded = Base64.getEncoder().encodeToString(
txt.getBytes(StandardCharsets.UTF_8));
String decoded = new String(Base64.getDecoder().decode(encoded),
StandardCharsets.UTF_8);
Streams
@Delabassee @JosePaumard#Devoxx #SE84EE
What is a Stream?
A new API
A typed interface
A new concept
@Delabassee @JosePaumard#Devoxx #SE84EE
What is a Stream?
A new API
A typed interface
A new concept
Let’s see some code!
@Delabassee @JosePaumard#Devoxx #SE84EE
Readable and efficient patterns
Extract histograms from data
List<Person> people = Arrays.asList();
Map<City, Double> agesByCity =
people.stream()
.filter(p -> p.getAge() > 20)
.collect(
Collectors.groupingBy(
Person::getCity,
Collectors.averagingDouble(Person::getAge)
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Readable and efficient patterns
Extract histograms from data
List<Person> people = Arrays.asList();
Map<City, Double> agesByCity =
people.stream()
.filter(p -> p.getAge() > 20)
.collect(
Collectors.groupingBy(
Person::getCity,
Collectors.averagingDouble(Person::getAge)
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Readable and efficient patterns
Extract histograms from data
List<Person> people = Arrays.asList();
Map<City, Double> agesByCity =
people.stream()
.filter(p -> p.getAge() > 20)
.collect(
Collectors.groupingBy(
Person::getCity,
Collectors.averagingDouble(Person::getAge)
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Readable and efficient patterns
Extract histograms from data
List<Person> people = Arrays.asList();
Map<City, Double> agesByCity =
people.stream()
.filter(p -> p.getAge() > 20)
.collect(
Collectors.groupingBy(
Person::getCity,
Collectors.averagingDouble(Person::getAge)
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Readable and efficient patterns
Extract histograms from data
List<Person> people = Arrays.asList();
Map<City, Double> agesByCity =
people.stream()
.filter(p -> p.getAge() > 20)
.collect(
Collectors.groupingBy(
Person::getCity,
Collectors.averagingDouble(Person::getAge)
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Readable and efficient patterns
Extract histograms from data
List<Person> people = Arrays.asList();
Map<City, Double> agesByCity =
people.stream()
.filter(p -> p.getAge() > 20)
.collect(
Collectors.groupingBy(
Person::getCity,
Collectors.averagingDouble(Person::getAge)
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Building a Stream on a String
Building a Stream on the letters of a String:
String s = "hello";
IntStream stream = s.chars(); // stream on the letters of s
@Delabassee @JosePaumard#Devoxx #SE84EE
Building a Stream on a String
Building a Stream on the letters of a String:
String s = "hello";
IntStream stream = s.chars(); // stream on the letters of s
s.chars() // IntStream
.mapToObj(letter -> (char)letter) // Stream<Character>
.map(Character::toUpperCase)
.forEach(System.out::println); // Prints a Character
> HELLO
@Delabassee @JosePaumard#Devoxx #SE84EE
Build a Stream from a text file
Find the first error line from a log file
// Java 7 : try with resources and use of Paths
Path path = Paths.get("d:", "tmp", "debug.log");
try (Stream<String> stream = Files.lines(path)) {
stream.filter(line -> line.contains("ERROR"))
.findFirst()
.ifPresent(System.out::println);
} catch (IOException ioe) {
// handle the exception
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Build a Stream from a regex
Building a Stream from a regexp
// book is a looooooooong String
Stream<String> words =
Pattern
.compile(" ")
.splitAsStream(book) ;
@Delabassee @JosePaumard#Devoxx #SE84EE
Build a Stream from a regex
Building a Stream from a regexp
More efficient than:
// book is a looooooooong String
Stream<String> words =
Pattern
.compile(" ")
.splitAsStream(book) ;
// book is a looooooooong String
Stream<String> words =
Stream.of(book.split(" "));
@Delabassee @JosePaumard#Devoxx #SE84EE
Flatmap: Files.lines + regex
Splitting a text files into words
Stream<String> streamOfLines = Files.lines(path);
Function<String, Stream<String>> splitter =
line -> Pattern.compile(" ").splitAsStream(line);
long numberOfWords =
streamOfLines.flatMap(splitter)
.map(String::toLowerCase)
.distinct()
.count();
@Delabassee @JosePaumard#Devoxx #SE84EE
Why is it important for Java EE?
Given this JSON file
[
{
"name":"Duke",
"gender":"m",
"phones":[
"home":"650‐123‐4567",
"mobile":"650‐111‐2222"
]
},
{
"name":"Jane", …
]
JsonArray contacts =
Json.createArrayBuilder()
.add(…
@Delabassee @JosePaumard#Devoxx #SE84EE
Why is it important for Java EE?
And some JSON-P magic
Map<String, Long> names =
contacts.getValuesAs(JsonObject.class).stream()
.filter(x -> "f".equals(x.getString("gender")))
.map(x -> (x.getString("name")))
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Why is it important for Java EE?
// Today
JsonArray names =
contacts.getValuesAs(JsonObject.class).stream()
.filter(x -> "f".equals(x.getString("gender")))
.map(x -> (x.getString("name")))
.collect(
Collector.of(
() -> Json.createArrayBuilder(),
(builder, value) -> builder.add(value),
(builder1, builder2) -> builder1.add(builder2),
builder -> builder.build()
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Why is it important for Java EE?
// Today
JsonArray names =
contacts.getValuesAs(JsonObject.class).stream()
.filter(x -> "f".equals(x.getString("gender")))
.map(x -> (x.getString("name")))
.collect(
Collector.of(
Json::createArrayBuilder,
JsonArrayBuilder::add,
JsonArrayBuilder::add,
JsonArrayBuilder::build
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Why is it important for Java EE?
// Today
JsonArray names =
contacts.getValuesAs(JsonObject.class).stream()
.filter(x -> "f".equals(x.getString("gender")))
.map(x -> (x.getString("name")))
.collect(
Collectors.collectingAndThen(
Collector.of(
Json::createArrayBuilder,
JsonArrayBuilder::add,
JsonArrayBuilder::add
),
JsonArrayBuilder::build
)
);
@Delabassee @JosePaumard#Devoxx #SE84EE
Why is it important for Java EE?
Collect into JSON
Avaiblable through the JsonCollectors factory
// Tomorrow, with JSON-P 1.1
JsonArray names =
contacts.getValuesAs(JsonObject.class).stream()
.filter(x -> "f".equals(x.getString("gender")))
.map(x -> (x.getString("name")))
.collect(
JsonCollectors.toJsonArray()
);
Parallelism
@Delabassee @JosePaumard#Devoxx #SE84EE
A warning on parallelism
All parallel operations (Streams, ConcurrentHashMap)
take place in the common ForkJoinPool
The common ForkJoinPool takes all the
available cores / CPUs
@Delabassee @JosePaumard#Devoxx #SE84EE
A warning on parallelism
All parallel operations (Streams, ConcurrentHashMap)
take place in the common ForkJoinPool
The common ForkJoinPool takes all the
available cores / CPUs
To preserve your Application Server:
System.setProperty(
"java.util.concurrent.ForkJoinPool.common.parallelism", "1") ;
Completable
Future
@Delabassee @JosePaumard#Devoxx #SE84EE
CompletableFuture
New addition to j.u.concurrent
Allow to chain asynchronous tasks
Use case: test the asynchronous calls in the Servlet API,
EJB, JAX-RS, …
@Delabassee @JosePaumard#Devoxx #SE84EE
Creation of an asynchronous task
The Jersey way to create an asynchronous call
@Path("/resource")
public class AsyncResource {
@GET
public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
new Thread(new Runnable() {
public void run() {
String result = longOperation();
asyncResponse.resume(result);
}
}).start();
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
Creation of an asynchronous task
(let us fix this code with Java 8)
@Path("/resource")
public class AsyncResource {
@GET
public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
executor.submit(() -> {
String result = longOperation();
asyncResponse.resume(result);
});
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
We want to check if the result object
is passed to the resume() method of the asyncResponse
It is a very basic test, but tricky to write since we are in an
asynchronous world
We have mocks for that!
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
We can inject a mock AsyncResponse, even mock the result
Then verify the correct interaction:
But we need to verify this once the run() method has been
called…
Mockito.verify(mockAsyncResponse).resume(result);
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
This is where CompletionStage come to the rescue!
@Path("/resource")
public class AsyncResource {
@Inject ExecutorService executor;
@GET
public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
executor.submit(() -> {
String result = longOperation();
asyncResponse.resume(result);
});
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
@Path("/resource")
public class AsyncResource {
@Inject ExecutorService executor;
@GET
public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
executeAsync(asyncResponse);
}
public CompletableFuture<Void> executeAsync(final AsyncResponse asyncResponse) {
return CompletableFuture.runAsync(() -> {
asyncResponse.resume(longOperation());
}, executor);
}
}
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
AsyncResource asyncResource = new AsyncResource();
asyncResource.executeAsync(mockAsyncResponse); // returns the CompletableFuture
.thenRun(() -> { // then execute this Runnable
Mockito.verify(mockAsyncResponse).resume(result);
}
);
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
Be careful of visibility issues
1) It’s better to run this in the same thread
2) Since the mocks are used and checked in this thread,
create them in this thread too
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
So the complete pattern becomes this one
1) First we create our mocks
String result = Mockito.mock(String.class);
AsyncResponse response = Mockito.mock(AsyncResponse.class);
Runnable train = () -> Mockito.doReturn(result).when(response.longOperation());
Runnable verify = () -> Mockito.verify(response).resume(result);
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
So the complete pattern becomes this one
2) Then we create the call & verify
Runnable callAndVerify = () -> {
asyncResource.executeAsync(response).thenRun(verify);
}
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
So the complete pattern becomes this one
3) Then we create the task
ExecutorService executor = Executors.newSingleThreadExecutor();
AsynResource asyncResource = new AsyncResource();
asyncResource.setExecutorService(executor);
CompletableFuture
.runAsync(train, executor) // this trains our mocks
.thenRun(callAndVerify); // this verifies our mocks
@Delabassee @JosePaumard#Devoxx #SE84EE
How to test it?
Since a CompletableFuture is also a Future, we can fail
with a timeout if the test does not complete fast enough
ExecutorService executor = Executors.newSingleThreadExecutor();
AsynResource asyncResource = new AsyncResource();
asyncResource.setExecutorService(executor);
CompletableFuture
.runAsync(train, executor) // this trains our mocks
.thenRun(callAndVerify) // this verifies our mocks
.get(10, TimeUnit.SECONDS);
@Delabassee @JosePaumard#Devoxx #SE84EE
Conclusion
Java 8 is not just about lambdas
There are also many new and very useful patterns
Ready to be used before becoming a lambda ninja!
Not covered:
- Collection framework
- Concurrency, Concurrent hashmap
- JavaScript on the JVM with Nashorn
- Security
@Delabassee @JosePaumard#SE84EE
Thank you!
@Delabassee @JosePaumard#SE84EE

Java SE 8 for Java EE developers

  • 1.
  • 2.
    @Delabassee @JosePaumard#Devoxx #SE84EE Agenda JavaSE 8 has been released ~1.5 year ago Java EE 7 Application Servers - GlassFish, WildFly, Liberty Profile, WebLogic, etc.
  • 3.
    @Delabassee @JosePaumard#Devoxx #SE84EE Agenda JavaSE 8 has been released ~1.5 year ago Java EE 7 Application Servers - GlassFish, WildFly, Liberty Profile, WebLogic, etc. Check some important Java SE 8 new features for Java EE developers... with patterns!
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
    The following isintended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle.
  • 10.
  • 11.
  • 12.
    @Delabassee @JosePaumard#Devoxx #SE84EE NewDate & Time API Date & Time API is a new introduction in Java 8 Initiated by Stephen Colebourne, based on Joda Time Complete replacement of Date & Calendar
  • 13.
    @Delabassee @JosePaumard#Devoxx #SE84EE Date:ZonedTime Useful for localized times Set<String> allZonesIds = ZoneId.getAvailableZoneIds() ; String ukTZ = ZoneId.of("Europe/Paris") ;
  • 14.
    @Delabassee @JosePaumard#Devoxx #SE84EE Date:ZonedTime Useful for localized times System.out.println( ZonedDateTime.of( 1998, Month.JULY.getValue(), 12, // year / month / day 22, 0, 0, 0, // h / mn / s / nanos ZoneId.of("Europe/Paris")) ); // prints 1998-07-22T22:00-00:01:15[Europe/London]
  • 15.
    @Delabassee @JosePaumard#Devoxx #SE84EE Date:ZonedTime Useful for localized times System.out.println( ZonedDateTime.of( 1998, Month.JULY.getValue(), 12, // year / month / day 22, 0, 0, 0, // h / mn / s / nanos ZoneId.of("Europe/Paris")) ); ZonedDateTime nextWorldCup = currentWorldCup.plus(Period.ofYear(4));
  • 16.
    @Delabassee @JosePaumard#Devoxx #SE84EE Date:ZonedTime Useful for localized times System.out.println( ZonedDateTime.of( 1998, Month.JULY.getValue(), 12, // year / month / day 22, 0, 0, 0, // h / mn / s / nanos ZoneId.of("Europe/Paris")) ); ZonedDateTime nextWorldCup = currentWorldCup.plus(Period.ofYear(4)); ZonedDateTime nexWorldCupJapan= nextWorldCup.withZoneSameInstant(ZoneId.of("Asia/Tokyo")) ;
  • 17.
    @Delabassee @JosePaumard#Devoxx #SE84EE Date:bridges with java.util.Date Conversions between j.u.Date and Date & Time Time time = Time.from(localTime); // API -> legacy LocalTime localTime = time.toLocalTime(); // legacy -> new API Date date = Date.from(localDate); // API -> legacy LocalDate localDate = date.toLocalDate(); // legacy -> new API TimeStamp time = TimeStamp.from(instant); // API -> legacy Instant instant = time.toInstant(); // legacy -> new API Date date = Date.from(instant); // API -> legacy Instant instant = date.toInstant(); // legacy -> new API
  • 18.
    @Delabassee @JosePaumard#Devoxx #SE84EE Usablein JPA JPA does not support this Date & Time API (yet) But with converters, we can still use it @Entity public abstract class AbstractPersistent { @Convert(converter=DateConverter.class) private Instant instant; }
  • 19.
    @Delabassee @JosePaumard#Devoxx #SE84EE Usablein JPA The converter is a classical object public class DateConverter implements AttributeConverter<Instant, Date> { public Date convertToDatabaseColumn(Instant instant) { return Date.from(instant); } public Instant convertToEntityAttribute(Date date) { return date.toInstant(); } }
  • 20.
    @Delabassee @JosePaumard#Devoxx #SE84EE Usablein JPA The converter is a classical object public class DateFormatConverter implements AttributeConverter<ZonedDateTime, String> { public String convertToDatabaseColumn(ZonedDateTime time) { return DateTimeFormatter.ISO_DATE_TIME.format(time); } public ZonedDateTime convertToEntityAttribute(String formated) { return DateTimeFormatter.ISO_DATE_TIME .parse(formated, ZonedDateTime::from); } }
  • 21.
    @Delabassee @JosePaumard#Devoxx #SE84EE Usablein JPA With JPA converters we can use the types from Date & Time API and map in j.u.l.Date or String
  • 22.
    @Delabassee @JosePaumard#Devoxx #SE84EE Usablein JSF With a Custom JSF Converter @FacesConverter("instantConverter") public class TimeConverter implements javax.faces.convert.Converter { @Override public Object getAsObject(FacesContext ctx, … , String value) { return Instant.parse(value); } }
  • 23.
    @Delabassee @JosePaumard#Devoxx #SE84EE Usablein JSF With a Custom JSF Converter... @Override public String getAsString(FacesContext ctx, … , Object value) { DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) .withLocale(Locale.US) .withZone(ZoneId.systemDefault()); return formatter.format((TemporalAccessor) value); } }
  • 24.
    @Delabassee @JosePaumard#Devoxx #SE84EE Usablein JSF With a Custom JSF Converter<h:form> <h:inputText id = "date” value = "#{order.timestamp}" size = "20" required="true” label = "start" converter = "instantConverter" /> ... @ManagedBean(name="order") @SessionScoped public class myBean implements Serializable{ Instant timestamp; …
  • 25.
  • 26.
    @Delabassee @JosePaumard#Devoxx #SE84EE Annotationsin Java 7 Wrapping annotations An annotation cannot be applied more than once @NamedQueries({ @NamedQuery(name=SELECT_ALL, query="..."), @NamedQuery(name=COUNT_ALL, query="...") }) public class Customer { }
  • 27.
    @Delabassee @JosePaumard#Devoxx #SE84EE Annotationsin Java 8 Java 8 makes it possible! @NamedQuery(name=SELECT_ALL, query="..."), @NamedQuery(name=COUNT_ALL, query="...") public class Customer { }
  • 28.
    @Delabassee @JosePaumard#Devoxx #SE84EE BeanValidation Suppose we want to validate a parameter public orderdCar order( @CheckCar("VW") Car aCar ) { ... }
  • 29.
    @Delabassee @JosePaumard#Devoxx #SE84EE BeanValidation Here is the Bean Validation code for @CheckCar @Target({PARAMETER}) @Retention(RUNTIME) @Constraint(validatedBy = CarValidator.class) public @interface CheckCar { Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String value(); }
  • 30.
    @Delabassee @JosePaumard#Devoxx #SE84EE BeanValidation And for the validator public class CarValidator implements ConstraintValidator<CheckCar, Car> { private String carBrand; public void initialize(CheckCar constraintAnnotation) { this.carBrand = constraintAnnotation.value(); } public boolean isValid(Car obj, ConstraintValidatorContext ctrCtx) { if (object == null) return true; return (!obj.getBrand().toUpperCase().contains(carBrand)); } }
  • 31.
    @Delabassee @JosePaumard#Devoxx #SE84EE BeanValidation And the Java 8 trick to make it repeatable @Target({PARAMETER}) @Retention(RUNTIME) @Repeatable(CheckCars.class) @Constraint(validatedBy = CarValidator.class) public @interface CheckCar { Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String value(); }
  • 32.
    @Delabassee @JosePaumard#Devoxx #SE84EE BeanValidation And the Java 8 trick to make it repeatable @Target({PARAMETER}) @Retention(RUNTIME) public @interface CheckCars { CheckCar[] value() default{}; }
  • 33.
    @Delabassee @JosePaumard#Devoxx #SE84EE BeanValidation Suppose we want to validate a parameter public orderdCar order( @CheckCar("VW") @ChechCar("Audi") Car aCar ) { ... }
  • 34.
    @Delabassee @JosePaumard#Devoxx #SE84EE Typeannotations Annotations can be now put on types Example 1: tell that a variable should not be null private @NonNull List<Person> persons = ... ;
  • 35.
    @Delabassee @JosePaumard#Devoxx #SE84EE Typeannotations Annotations can be now put on types Example 1: tell that a variable should not be null Example 2: the content should not be null neither private @NonNull List<Person> persons = ... ; private @NonNull List<@NonNull Person> persons = ... ;
  • 36.
  • 37.
    @Delabassee @JosePaumard#Devoxx #SE84EE Joiningstrings New String joiners are coming to the JDK!
  • 38.
    @Delabassee @JosePaumard#Devoxx #SE84EE Joiningstrings StringJoiner class And it prints: StringJoiner sj = new StringJoiner(", ") ; sj.add("one").add("two").add("three") ; String s = sj.toString() ; System.out.println(s) ; > one, two, three
  • 39.
    @Delabassee @JosePaumard#Devoxx #SE84EE Joiningstrings StringJoiner with prefix / postfix And it prints: StringJoiner sj = new StringJoiner(", ", "{", "}") ; sj.add("one").add("two").add("three") ; String s = sj.toString() ; System.out.println(s) ; > {one, two, three}
  • 40.
    @Delabassee @JosePaumard#Devoxx #SE84EE Joiningstrings Also accessible from the String class And it prints: // From the String class, with a vararg String s = String.join(", ", "one", "two", "three"); System.out.println(s); > one, two, three
  • 41.
    @Delabassee @JosePaumard#Devoxx #SE84EE Joiningstrings And directly from a List // From a list to a String using the Stream & Collectors API String s = listOfStrings.stream() .collect(Collectors.joining(", ")); System.out.println(s); > one, two, three
  • 42.
    @Delabassee @JosePaumard#Devoxx #SE84EE Joiningstrings And directly from a List The result is // From the String class, with a vararg String s = listOfStrings.stream() .collect(Collectors.joining(", ", "{", "}")); System.out.println(s); > {one, two, three}
  • 43.
    @Delabassee @JosePaumard#Devoxx #SE84EE Supportfor Base64 decoding After a few years… import java.util.Base64; String txt = "Modern Times!"; String encoded = Base64.getEncoder().encodeToString( txt.getBytes(StandardCharsets.UTF_8)); String decoded = new String(Base64.getDecoder().decode(encoded), StandardCharsets.UTF_8);
  • 44.
  • 45.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whatis a Stream? A new API A typed interface A new concept
  • 46.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whatis a Stream? A new API A typed interface A new concept Let’s see some code!
  • 47.
    @Delabassee @JosePaumard#Devoxx #SE84EE Readableand efficient patterns Extract histograms from data List<Person> people = Arrays.asList(); Map<City, Double> agesByCity = people.stream() .filter(p -> p.getAge() > 20) .collect( Collectors.groupingBy( Person::getCity, Collectors.averagingDouble(Person::getAge) ) );
  • 48.
    @Delabassee @JosePaumard#Devoxx #SE84EE Readableand efficient patterns Extract histograms from data List<Person> people = Arrays.asList(); Map<City, Double> agesByCity = people.stream() .filter(p -> p.getAge() > 20) .collect( Collectors.groupingBy( Person::getCity, Collectors.averagingDouble(Person::getAge) ) );
  • 49.
    @Delabassee @JosePaumard#Devoxx #SE84EE Readableand efficient patterns Extract histograms from data List<Person> people = Arrays.asList(); Map<City, Double> agesByCity = people.stream() .filter(p -> p.getAge() > 20) .collect( Collectors.groupingBy( Person::getCity, Collectors.averagingDouble(Person::getAge) ) );
  • 50.
    @Delabassee @JosePaumard#Devoxx #SE84EE Readableand efficient patterns Extract histograms from data List<Person> people = Arrays.asList(); Map<City, Double> agesByCity = people.stream() .filter(p -> p.getAge() > 20) .collect( Collectors.groupingBy( Person::getCity, Collectors.averagingDouble(Person::getAge) ) );
  • 51.
    @Delabassee @JosePaumard#Devoxx #SE84EE Readableand efficient patterns Extract histograms from data List<Person> people = Arrays.asList(); Map<City, Double> agesByCity = people.stream() .filter(p -> p.getAge() > 20) .collect( Collectors.groupingBy( Person::getCity, Collectors.averagingDouble(Person::getAge) ) );
  • 52.
    @Delabassee @JosePaumard#Devoxx #SE84EE Readableand efficient patterns Extract histograms from data List<Person> people = Arrays.asList(); Map<City, Double> agesByCity = people.stream() .filter(p -> p.getAge() > 20) .collect( Collectors.groupingBy( Person::getCity, Collectors.averagingDouble(Person::getAge) ) );
  • 53.
    @Delabassee @JosePaumard#Devoxx #SE84EE Buildinga Stream on a String Building a Stream on the letters of a String: String s = "hello"; IntStream stream = s.chars(); // stream on the letters of s
  • 54.
    @Delabassee @JosePaumard#Devoxx #SE84EE Buildinga Stream on a String Building a Stream on the letters of a String: String s = "hello"; IntStream stream = s.chars(); // stream on the letters of s s.chars() // IntStream .mapToObj(letter -> (char)letter) // Stream<Character> .map(Character::toUpperCase) .forEach(System.out::println); // Prints a Character > HELLO
  • 55.
    @Delabassee @JosePaumard#Devoxx #SE84EE Builda Stream from a text file Find the first error line from a log file // Java 7 : try with resources and use of Paths Path path = Paths.get("d:", "tmp", "debug.log"); try (Stream<String> stream = Files.lines(path)) { stream.filter(line -> line.contains("ERROR")) .findFirst() .ifPresent(System.out::println); } catch (IOException ioe) { // handle the exception }
  • 56.
    @Delabassee @JosePaumard#Devoxx #SE84EE Builda Stream from a regex Building a Stream from a regexp // book is a looooooooong String Stream<String> words = Pattern .compile(" ") .splitAsStream(book) ;
  • 57.
    @Delabassee @JosePaumard#Devoxx #SE84EE Builda Stream from a regex Building a Stream from a regexp More efficient than: // book is a looooooooong String Stream<String> words = Pattern .compile(" ") .splitAsStream(book) ; // book is a looooooooong String Stream<String> words = Stream.of(book.split(" "));
  • 58.
    @Delabassee @JosePaumard#Devoxx #SE84EE Flatmap:Files.lines + regex Splitting a text files into words Stream<String> streamOfLines = Files.lines(path); Function<String, Stream<String>> splitter = line -> Pattern.compile(" ").splitAsStream(line); long numberOfWords = streamOfLines.flatMap(splitter) .map(String::toLowerCase) .distinct() .count();
  • 59.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whyis it important for Java EE? Given this JSON file [ { "name":"Duke", "gender":"m", "phones":[ "home":"650‐123‐4567", "mobile":"650‐111‐2222" ] }, { "name":"Jane", … ] JsonArray contacts = Json.createArrayBuilder() .add(…
  • 60.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whyis it important for Java EE? And some JSON-P magic Map<String, Long> names = contacts.getValuesAs(JsonObject.class).stream() .filter(x -> "f".equals(x.getString("gender"))) .map(x -> (x.getString("name"))) .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) );
  • 61.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whyis it important for Java EE? // Today JsonArray names = contacts.getValuesAs(JsonObject.class).stream() .filter(x -> "f".equals(x.getString("gender"))) .map(x -> (x.getString("name"))) .collect( Collector.of( () -> Json.createArrayBuilder(), (builder, value) -> builder.add(value), (builder1, builder2) -> builder1.add(builder2), builder -> builder.build() ) );
  • 62.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whyis it important for Java EE? // Today JsonArray names = contacts.getValuesAs(JsonObject.class).stream() .filter(x -> "f".equals(x.getString("gender"))) .map(x -> (x.getString("name"))) .collect( Collector.of( Json::createArrayBuilder, JsonArrayBuilder::add, JsonArrayBuilder::add, JsonArrayBuilder::build ) );
  • 63.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whyis it important for Java EE? // Today JsonArray names = contacts.getValuesAs(JsonObject.class).stream() .filter(x -> "f".equals(x.getString("gender"))) .map(x -> (x.getString("name"))) .collect( Collectors.collectingAndThen( Collector.of( Json::createArrayBuilder, JsonArrayBuilder::add, JsonArrayBuilder::add ), JsonArrayBuilder::build ) );
  • 64.
    @Delabassee @JosePaumard#Devoxx #SE84EE Whyis it important for Java EE? Collect into JSON Avaiblable through the JsonCollectors factory // Tomorrow, with JSON-P 1.1 JsonArray names = contacts.getValuesAs(JsonObject.class).stream() .filter(x -> "f".equals(x.getString("gender"))) .map(x -> (x.getString("name"))) .collect( JsonCollectors.toJsonArray() );
  • 65.
  • 66.
    @Delabassee @JosePaumard#Devoxx #SE84EE Awarning on parallelism All parallel operations (Streams, ConcurrentHashMap) take place in the common ForkJoinPool The common ForkJoinPool takes all the available cores / CPUs
  • 67.
    @Delabassee @JosePaumard#Devoxx #SE84EE Awarning on parallelism All parallel operations (Streams, ConcurrentHashMap) take place in the common ForkJoinPool The common ForkJoinPool takes all the available cores / CPUs To preserve your Application Server: System.setProperty( "java.util.concurrent.ForkJoinPool.common.parallelism", "1") ;
  • 68.
  • 69.
    @Delabassee @JosePaumard#Devoxx #SE84EE CompletableFuture Newaddition to j.u.concurrent Allow to chain asynchronous tasks Use case: test the asynchronous calls in the Servlet API, EJB, JAX-RS, …
  • 70.
    @Delabassee @JosePaumard#Devoxx #SE84EE Creationof an asynchronous task The Jersey way to create an asynchronous call @Path("/resource") public class AsyncResource { @GET public void asyncGet(@Suspended final AsyncResponse asyncResponse) { new Thread(new Runnable() { public void run() { String result = longOperation(); asyncResponse.resume(result); } }).start(); } }
  • 71.
    @Delabassee @JosePaumard#Devoxx #SE84EE Creationof an asynchronous task (let us fix this code with Java 8) @Path("/resource") public class AsyncResource { @GET public void asyncGet(@Suspended final AsyncResponse asyncResponse) { executor.submit(() -> { String result = longOperation(); asyncResponse.resume(result); }); } }
  • 72.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? We want to check if the result object is passed to the resume() method of the asyncResponse It is a very basic test, but tricky to write since we are in an asynchronous world We have mocks for that!
  • 73.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? We can inject a mock AsyncResponse, even mock the result Then verify the correct interaction: But we need to verify this once the run() method has been called… Mockito.verify(mockAsyncResponse).resume(result);
  • 74.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? This is where CompletionStage come to the rescue! @Path("/resource") public class AsyncResource { @Inject ExecutorService executor; @GET public void asyncGet(@Suspended final AsyncResponse asyncResponse) { executor.submit(() -> { String result = longOperation(); asyncResponse.resume(result); }); } }
  • 75.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? @Path("/resource") public class AsyncResource { @Inject ExecutorService executor; @GET public void asyncGet(@Suspended final AsyncResponse asyncResponse) { executeAsync(asyncResponse); } public CompletableFuture<Void> executeAsync(final AsyncResponse asyncResponse) { return CompletableFuture.runAsync(() -> { asyncResponse.resume(longOperation()); }, executor); } }
  • 76.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? AsyncResource asyncResource = new AsyncResource(); asyncResource.executeAsync(mockAsyncResponse); // returns the CompletableFuture .thenRun(() -> { // then execute this Runnable Mockito.verify(mockAsyncResponse).resume(result); } );
  • 77.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? Be careful of visibility issues 1) It’s better to run this in the same thread 2) Since the mocks are used and checked in this thread, create them in this thread too
  • 78.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? So the complete pattern becomes this one 1) First we create our mocks String result = Mockito.mock(String.class); AsyncResponse response = Mockito.mock(AsyncResponse.class); Runnable train = () -> Mockito.doReturn(result).when(response.longOperation()); Runnable verify = () -> Mockito.verify(response).resume(result);
  • 79.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? So the complete pattern becomes this one 2) Then we create the call & verify Runnable callAndVerify = () -> { asyncResource.executeAsync(response).thenRun(verify); }
  • 80.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? So the complete pattern becomes this one 3) Then we create the task ExecutorService executor = Executors.newSingleThreadExecutor(); AsynResource asyncResource = new AsyncResource(); asyncResource.setExecutorService(executor); CompletableFuture .runAsync(train, executor) // this trains our mocks .thenRun(callAndVerify); // this verifies our mocks
  • 81.
    @Delabassee @JosePaumard#Devoxx #SE84EE Howto test it? Since a CompletableFuture is also a Future, we can fail with a timeout if the test does not complete fast enough ExecutorService executor = Executors.newSingleThreadExecutor(); AsynResource asyncResource = new AsyncResource(); asyncResource.setExecutorService(executor); CompletableFuture .runAsync(train, executor) // this trains our mocks .thenRun(callAndVerify) // this verifies our mocks .get(10, TimeUnit.SECONDS);
  • 82.
    @Delabassee @JosePaumard#Devoxx #SE84EE Conclusion Java8 is not just about lambdas There are also many new and very useful patterns Ready to be used before becoming a lambda ninja! Not covered: - Collection framework - Concurrency, Concurrent hashmap - JavaScript on the JVM with Nashorn - Security
  • 83.
  • 84.