SlideShare a Scribd company logo
1 of 32
Download to read offline
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.
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.
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.
<?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.
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
Source Listing - pom.xml
codenameone.com github.com/codenameone/CodenameOne
This declares the h2 database instead of mysql
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
Source Listing - pom.xml
codenameone.com github.com/codenameone/CodenameOne
And here we declare the use of lombok which I’ll get into shortly
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
Source Listing - pom.xml
codenameone.com github.com/codenameone/CodenameOne
I also added a dependency on Apache commons IO which is pretty useful for small utility calls.
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.
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.
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).
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.
@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.
@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
@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.
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.
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.
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…
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.
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…
@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
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…
}
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.
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.
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
}
@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.
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.
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.
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.
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.
codenameone.com github.com/codenameone/CodenameOne
Thank You
Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
Thank You
Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
Thanks for watching I hope you’ll enjoy the rest of the course and find it educational

More Related Content

Similar to create-netflix-clone-02-server_transcript.pdf

RubyMotion Inspect Conference - 2013. (With speaker notes.)
RubyMotion Inspect Conference - 2013. (With speaker notes.)RubyMotion Inspect Conference - 2013. (With speaker notes.)
RubyMotion Inspect Conference - 2013. (With speaker notes.)alloy020
 
Creating a Facebook Clone - Part XVII - Transcript.pdf
Creating a Facebook Clone - Part XVII - Transcript.pdfCreating a Facebook Clone - Part XVII - Transcript.pdf
Creating a Facebook Clone - Part XVII - Transcript.pdfShaiAlmog1
 
I Heard React Was Good
I Heard React Was GoodI Heard React Was Good
I Heard React Was GoodFITC
 
Fame
FameFame
Famerpatil82
 
Gnizr Architecture (for developers)
Gnizr Architecture (for developers)Gnizr Architecture (for developers)
Gnizr Architecture (for developers)hchen1
 
PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...
PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...
PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...Puppet
 
Web technologies-course 12.pptx
Web technologies-course 12.pptxWeb technologies-course 12.pptx
Web technologies-course 12.pptxStefan Oprea
 
The Basic Concept Of IOC
The Basic Concept Of IOCThe Basic Concept Of IOC
The Basic Concept Of IOCCarl Lu
 
Ionic framework one day training
Ionic framework one day trainingIonic framework one day training
Ionic framework one day trainingTroy Miles
 
Plugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGemsPlugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGemsSadayuki Furuhashi
 
Lightning web components
Lightning web components Lightning web components
Lightning web components Cloud Analogy
 
The complete-beginners-guide-to-react dyrr
The complete-beginners-guide-to-react dyrrThe complete-beginners-guide-to-react dyrr
The complete-beginners-guide-to-react dyrrAfreenK
 
Sbt, idea and eclipse
Sbt, idea and eclipseSbt, idea and eclipse
Sbt, idea and eclipseMike Slinn
 
Handling Database Deployments
Handling Database DeploymentsHandling Database Deployments
Handling Database DeploymentsMike Willbanks
 
React Django Presentation
React Django PresentationReact Django Presentation
React Django PresentationAllison DiNapoli
 
OpenDMS - the first 2 weeks
OpenDMS - the first 2 weeksOpenDMS - the first 2 weeks
OpenDMS - the first 2 weeksJPC Hanson
 

Similar to create-netflix-clone-02-server_transcript.pdf (20)

RubyMotion Inspect Conference - 2013. (With speaker notes.)
RubyMotion Inspect Conference - 2013. (With speaker notes.)RubyMotion Inspect Conference - 2013. (With speaker notes.)
RubyMotion Inspect Conference - 2013. (With speaker notes.)
 
Creating a Facebook Clone - Part XVII - Transcript.pdf
Creating a Facebook Clone - Part XVII - Transcript.pdfCreating a Facebook Clone - Part XVII - Transcript.pdf
Creating a Facebook Clone - Part XVII - Transcript.pdf
 
I Heard React Was Good
I Heard React Was GoodI Heard React Was Good
I Heard React Was Good
 
Fame
FameFame
Fame
 
Gnizr Architecture (for developers)
Gnizr Architecture (for developers)Gnizr Architecture (for developers)
Gnizr Architecture (for developers)
 
PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...
PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...
PuppetConf 2016: Writing Custom Types to Manage Web-Based Applications – Tim ...
 
Web technologies-course 12.pptx
Web technologies-course 12.pptxWeb technologies-course 12.pptx
Web technologies-course 12.pptx
 
Jsp and jstl
Jsp and jstlJsp and jstl
Jsp and jstl
 
rails.html
rails.htmlrails.html
rails.html
 
rails.html
rails.htmlrails.html
rails.html
 
The Basic Concept Of IOC
The Basic Concept Of IOCThe Basic Concept Of IOC
The Basic Concept Of IOC
 
Ionic framework one day training
Ionic framework one day trainingIonic framework one day training
Ionic framework one day training
 
Plugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGemsPlugin-based software design with Ruby and RubyGems
Plugin-based software design with Ruby and RubyGems
 
Lightning web components
Lightning web components Lightning web components
Lightning web components
 
The complete-beginners-guide-to-react dyrr
The complete-beginners-guide-to-react dyrrThe complete-beginners-guide-to-react dyrr
The complete-beginners-guide-to-react dyrr
 
Sbt, idea and eclipse
Sbt, idea and eclipseSbt, idea and eclipse
Sbt, idea and eclipse
 
Lombok
LombokLombok
Lombok
 
Handling Database Deployments
Handling Database DeploymentsHandling Database Deployments
Handling Database Deployments
 
React Django Presentation
React Django PresentationReact Django Presentation
React Django Presentation
 
OpenDMS - the first 2 weeks
OpenDMS - the first 2 weeksOpenDMS - the first 2 weeks
OpenDMS - the first 2 weeks
 

More from ShaiAlmog1

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...ShaiAlmog1
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfShaiAlmog1
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfShaiAlmog1
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfShaiAlmog1
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfShaiAlmog1
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfCreating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfCreating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfShaiAlmog1
 

More from ShaiAlmog1 (20)

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdf
 
Creating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfCreating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdf
 
Creating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfCreating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdf
 

Recently uploaded

Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 

Recently uploaded (20)

Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
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.
  • 5. <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> Source Listing - pom.xml codenameone.com github.com/codenameone/CodenameOne This declares the h2 database instead of mysql
  • 6. </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> Source Listing - pom.xml codenameone.com github.com/codenameone/CodenameOne And here we declare the use of lombok which I’ll get into shortly
  • 7. </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> Source Listing - pom.xml codenameone.com github.com/codenameone/CodenameOne I also added a dependency on Apache commons IO which is pretty useful for small utility calls.
  • 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.
  • 30. codenameone.com github.com/codenameone/CodenameOne Thank You Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
  • 31. Thank You Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
  • 32. Thanks for watching I hope you’ll enjoy the rest of the course and find it educational