Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Â
create-netflix-clone-02-server_transcript.pdf
1. Creating a Netflix Clone
II
In this lesson we’ll go over the basic server architecture and project Lombok which we use in the server implementation. We’ll start with some basic information about the
server and then dive into a quick lombok overview. 

So first let’s talk about the database. Unlike previous modules where I chose to use mysql, this time around I picked h2 for the database since it requires literally no
setup. I wouldn’t use it for production but since the whole complexity of mysql is covered in other modules this just isn’t necessary.
2. codenameone.com github.com/codenameone/CodenameOne
Server and
Lombok
Introduction
In this lesson we’ll go over the basic server architecture and project Lombok which we use in the server implementation. We’ll start with some basic information about the
server and then dive into a quick lombok overview. 

So first let’s talk about the database. Unlike previous modules where I chose to use mysql, this time around I picked h2 for the database since it requires literally no
setup. I wouldn’t use it for production but since the whole complexity of mysql is covered in other modules this just isn’t necessary.
3. codenameone.com github.com/codenameone/CodenameOne
Server and Lombok Introduction
We use H2 for simplicity as usual we use JPA for persistence
Lombok is used for entities and DTOs
There’s only one webservice
We don’t need more without authentication and other complexities
In this lesson we’ll go over the basic server architecture and project Lombok which we use in the server implementation. We’ll start with some basic information about the
server and then dive into a quick lombok overview. 

So first let’s talk about the database. Unlike previous modules where I chose to use mysql, this time around I picked h2 for the database since it requires literally no
setup. I wouldn’t use it for production but since the whole complexity of mysql is covered in other modules this just isn’t necessary.
Lombok is used to make the server code tiny and devoid of boilerplate. This is pretty cool.
We don’t really need much in terms of webservices. If we had authentication and authorisation there would have been more. I could also implement paging support and
more complex requests for various segments of the UI but those are pretty obvious if you’ve gone over the feed section of the facebook clone.
The authentication aspect is the big missing piece here and I just didn’t want to get into it. It’s usually one of the more painful aspects of building a server but since this is
a mobile course I didn’t think it’s very important to cover again as it was covered in previous modules.
4. <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/
maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.codename1.demos</groupId>
<artifactId>netflixclone</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>NetflixCloneServer</name>
<description>Netflix Clone Sample</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Source Listing - pom.xml
codenameone.com github.com/codenameone/CodenameOne
Let’s start with the POM file, this is all pretty minimal.
First we have the standard declarations, we use spring boot 2.2.2 which is the current version at this time. This code is generated with the spring intializer which I
described before.
8. codenameone.com github.com/codenameone/CodenameOne
Lombok
Java language enhancement tool
Requires IDE plugin
Removes boilerplate getters/setters
Works with Codename One as well but not as valuable thanks to properties
See https://www.codenameone.com/blog/tip-using-lombok-other-tools.html
Lombok is a set of tools that
include IDE plugins, libraries and runtime tools.
They essentially try to modernise Java syntax using special annotations and it does a pretty great job at removing a lot of the common boilerplate from Java 8 syntax.
Their biggest claim to fame is in removing the getter & setter boilerplate code from Java.
In this module we’ll use Lombok in the server. It works for Codename One app code but we won’t touch on that.
The main reason for that is that the value of Lombok diminishes thanks to properties so we don’t need it as much. But if you need it you can see this tip about installing
Lomobok for a Codename One app.
9. public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
}
public static <T> ConstructorExample<T> of(T description) {
return new ConstructorExample<T>(description);
}
@java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
Source Listing - Lombok: Constructors
codenameone.com github.com/codenameone/CodenameOne
Let’s look at a few examples of using Lombok. Notice that these examples are picked directly from the Lombok developer guide.
Here we have a class with 3 fields but only one of them is marked as NonNull. As such we can’t construct this object without the description field as we would have an
invalid object. So we have a private constructor that accepts this required field. To create the actual instance of this class we use the “of” method which accepts the
required description argument. So you would be able to just write ConstructorExample.of(description). That’s pretty nice. But it took 5 lines of code not including curly
braces or spaces… That’s a bit verbose.
10. this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
}
}
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
@NonNull
private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull
private String field;
}
}
Source Listing - Lombok: Constructors
codenameone.com github.com/codenameone/CodenameOne
That can be achieved with one annotation in Lombok. You just define the constructor and the method name that you wish to add as a static factory method and voila. It
works exactly like the code we saw before. You can literally write ConstructorExample.of(description).
11. public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
}
public static <T> ConstructorExample<T> of(T description) {
return new ConstructorExample<T>(description);
}
@java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
Source Listing - Lombok: Constructors
codenameone.com github.com/codenameone/CodenameOne
The other constructor is for subclasses. It accepts all the state members and also makes sure to fail if we try to violate the not null annotation.
Notice it’s scoped as protected so it would be used only when deriving the class.
12. @java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
}
}
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
@NonNull
private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull
private String field;
}
}
Source Listing - Lombok: Constructors
codenameone.com github.com/codenameone/CodenameOne
This can be implemented with a single line of code. The AllArgsConstructor annotation does all of that implicitly. It also has an optional access level property which
defaults to public.
13. @java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
}
}
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
@NonNull
private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull
private String field;
}
}
Source Listing - Lombok: Constructors
codenameone.com github.com/codenameone/CodenameOne
The inner class is pretty simple. There isn’t too much to save here but still there’s a bit of verbosity
14. @RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
@NonNull
private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull
private String field;
}
}
Source Listing - Lombok: Constructors
codenameone.com github.com/codenameone/CodenameOne
We can implement the blank constructor using the NoArgsConstructor. Notice that this sample is a bit synthetic. Normally we would use this annotation in conjunction
with other constructor options to indicate that we also want that option.
15. public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked @NonNull but is null");
}
this.name = person.getName();
}
}
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
Source Listing - Lombok: NonNull
codenameone.com github.com/codenameone/CodenameOne
We already saw non null being used before so this example should come as no surprise. This annotation can also apply to method arguments etc.
16. public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked @NonNull but is null");
}
this.name = person.getName();
}
}
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
Source Listing - Lombok: NonNull
codenameone.com github.com/codenameone/CodenameOne
The method can now assume the variable isn’t null. Notice that this usage is a bit stupid as the person.getName() call will throw a NullPointerException anyway but if you
invoke code that might propagate null it could be useful.
17. public class CleanupExample {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream(args[0]);
try {
OutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
}
}
public class CleanupExample {
public static void main(String[] args) throws IOException {
Source Listing - Lombok: Cleanup
codenameone.com github.com/codenameone/CodenameOne
Let’s move on to another cool feature of lombok. Notice that this code can be improved by using the Java 8 try with resources syntax. So this isn’t as beneficial but it’s
still pretty cool…
18. if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
}
}
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
Source Listing - Lombok: Cleanup
codenameone.com github.com/codenameone/CodenameOne
That block can be written like this which is as terse as the try with resources code and possibly even more terse.
19. public class GetterSetterExample {
private int age = 10;
private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
protected void setName(String name) {
this.name = name;
}
}
public class GetterSetterExample {
@Getter @Setter private int age = 10;
@Setter(AccessLevel.PROTECTED) private String name;
Source Listing - Lombok: Getters & Setters
codenameone.com github.com/codenameone/CodenameOne
Lomboks claim to fame has always been getter and setter elimination. So this whole block of code can be replaced with…
20. @Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
protected void setName(String name) {
this.name = name;
}
}
public class GetterSetterExample {
@Getter @Setter private int age = 10;
@Setter(AccessLevel.PROTECTED) private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
}
Source Listing - Lombok: Getters & Setters
codenameone.com github.com/codenameone/CodenameOne
This. Notice that this is still relatively verbose as we want a protected setter
21. public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
public DataExample(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setScore(double score) {
this.score = score;
}
public double getScore() {
Source Listing - Lombok: Data (and more…)
codenameone.com github.com/codenameone/CodenameOne
So let’s see something that’s even more terse. First notice that the setter for age as package protected access while the getter is public…
22. }
public void setTags(String[] tags) {
this.tags = tags;
}
@Override public String toString() {
return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() +
", " + Arrays.deepToString(this.getTags()) + ")";
}
protected boolean canEqual(Object other) {
return other instanceof DataExample;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof DataExample)) return false;
DataExample other = (DataExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !
this.getName().equals(other.getName())) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.getScore());
Source Listing - Lombok: Data (and more…)
codenameone.com github.com/codenameone/CodenameOne
Also check out all the boilerplate code we have for equals and toString. This can be optimised with some of the newer Objects class methods but not by much.
23. if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.getScore());
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + this.getAge();
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
return result;
}
public static class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(name, value);
}
public String getName() {
return this.name;
Source Listing - Lombok: Data (and more…)
codenameone.com github.com/codenameone/CodenameOne
The boilerplate doesn’t end though, we have a dashcode method too and a non-trivial inner class with a static creation method.
24. result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
return result;
}
public static class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@Override public String toString() {
return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
}
protected boolean canEqual(Object other) {
return other instanceof Exercise;
Source Listing - Lombok: Data (and more…)
codenameone.com github.com/codenameone/CodenameOne
Notice that this is the @RequiredArgsConstructor syntax we mentioned before
25. }
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + (this.getValue() == null ? 43 :
this.getValue().hashCode());
return result;
}
}
}
@Data public class DataExample {
private final String name;
@Setter(AccessLevel.PACKAGE) private int age;
private double score;
private String[] tags;
@ToString(includeFieldNames=true)
@Data(staticConstructor="of")
public static class Exercise<T> {
private final String name;
private final T value;
}
}
Source Listing - Lombok: Data (and more…)
codenameone.com github.com/codenameone/CodenameOne
That code that includes pages of data can be achieved using the @Data annotation. It includes getters, setters, toString and hashCode implicitly. Notice you can explicitly
override the definition of a specific setter from data as we did for the case of age.
26. public class ValVarExample {
public String example() {
final ArrayList<String> example = new ArrayList<String>();
example.add("Hello, World!");
int varExample = 3;
ArrayList<String> varExample = new ArrayList<String>();
varExample = new ArrayList<String>();
final String foo = example.get(0);
return foo.toLowerCase();
}
}
public class ValVarExample {
public String example() {
val example = new ArrayList<String>();
example.add("Hello, World!");
var varExample = new ArrayList<String>();
varExample = new ArrayList<String>();
val foo = example.get(0);
return foo.toLowerCase();
}
}
Source Listing - Lombok: val/var
codenameone.com github.com/codenameone/CodenameOne
Another common task is variable definition. Again there’s a lot of boilerplate here. So much that Java defined a new val keyword. But it isn’t yet available for Java 8 which
is used by most of us.
27. public class ValVarExample {
public String example() {
final ArrayList<String> example = new ArrayList<String>();
example.add("Hello, World!");
int varExample = 3;
ArrayList<String> varExample = new ArrayList<String>();
varExample = new ArrayList<String>();
final String foo = example.get(0);
return foo.toLowerCase();
}
}
public class ValVarExample {
public String example() {
val example = new ArrayList<String>();
example.add("Hello, World!");
var varExample = new ArrayList<String>();
varExample = new ArrayList<String>();
val foo = example.get(0);
return foo.toLowerCase();
}
}
Source Listing - Lombok: val/var
codenameone.com github.com/codenameone/CodenameOne
Lombok added two keywords: val and var.
Var lets us define a variable that can change. A mutable variable. Val defines an immutable variable. Effectively a final variable.
28. codenameone.com github.com/codenameone/CodenameOne
Lombok: There are a lot of features we didn’t cover…
@Value
@Builder
@SneakyThrows
@Synchronized
@With
There are a lot of annotations we didn’t cover here. @Value is the immutable variant of @Data; all fields are made private and final by default, and setters are not
generated. The class itself is also made final by default, because immutability is not something that can be forced onto a subclass. Just like in data the toString(), equals()
and hashCode() methods are also generated. Each field gets a getter method, and a constructor that covers every argument is also generated.
The @Builder annotation produces complex builder APIs for your classes. @Builder lets you automatically produce the code required to have your class be instantiable
with code such as: Person.builder().name("Shai").build(); Notice this works nicely with the @Value annotation to produce immutable classes with the builder pattern.
@SneakyThrows can be used to sneakily throw checked exceptions without actually declaring this in your method's throws clause. Since checked exceptions are a
feature of the Java language and not of the Java bytecode this is technically possible.
@Synchronized is a safer variant of the synchronized method modifier. The synchronized keyword locks on this object which is problematic as it exposes the lock state
externally. This annotation implicitly creates a hidden $lock object and synchronizes on that object.
The next best alternative to a setter for an immutable property is to construct a clone of the object, but with a new value for this one field. A method to generate this
clone is precisely what @With generates: a withFieldName(newValue) method which produces a clone except for the new value for the associated field.
29. codenameone.com github.com/codenameone/CodenameOne
Lombok: There are a lot of features we didn’t cover…
@Log
@Getter(lazy=true)
You can learn more at https://projectlombok.org/features/all
You put @Log on your class you then have a static final log field, initialized as is the commonly prescribed way for the logging framework you use, which you can then
use to write log statements. Notice that there are a lot of annotations you can use to describe the explicit log system you want to use in the project.
You can let lombok generate a getter which will calculate a value once, the first time this getter is called, and cache it from then on. This can be useful if calculating the
value takes a lot of CPU, or the value takes a lot of memory. To use this feature, create a private final variable, initialize it with the expression that's expensive to run, and
annotate your field with @Getter(lazy=true). The field will be hidden from the rest of your code, and the expression will be evaluated no more than once, when the getter is
first called. There are no magic marker values (i.e. even if the result of your expensive calculation is null, the result is cached) and your expensive calculation need not be
thread-safe, as lombok takes care of locking.
Most of this is taken directly from https://projectlombok.org/features/all but there’s a lot more information there.