SlideShare a Scribd company logo
Creating a Netflix Clone
IV
In this final instalment covering the server we’ll go over the service classes and the final entity representing the content creation. The content collection is an over
simplification of the concept. A site like Netflix would probably generate this data dynamically based on user viewing preferences and complex heuristics. I just
hardcoded an entity which was simpler.
codenameone.com github.com/codenameone/CodenameOne
Server
Continued
In this final instalment covering the server we’ll go over the service classes and the final entity representing the content creation. The content collection is an over
simplification of the concept. A site like Netflix would probably generate this data dynamically based on user viewing preferences and complex heuristics. I just
hardcoded an entity which was simpler.
codenameone.com github.com/codenameone/CodenameOne
Server Continued
We have one last entity to cover, the ContentCollection
Then we cover the service class
In this final instalment covering the server we’ll go over the service classes and the final entity representing the content creation. The content collection is an over
simplification of the concept. A site like Netflix would probably generate this data dynamically based on user viewing preferences and complex heuristics. I just
hardcoded an entity which was simpler.

The service class doesn’t do anything other than create the builtin data and implements the basic service call.
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ContentCollection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
private Content lead;
@ManyToMany
private Set<Content> popular;
@ManyToMany
private Set<Content> myList;
@ManyToMany
private Set<Content> recommended;
public ContentCollectionDTO toDTO() {
return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended));
}
private List<ContentDTO> asDTO(Set<Content> cnt) {
Source Listing - ContentCollection
codenameone.com github.com/codenameone/CodenameOne
The content collection starts similarly to the other entities
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ContentCollection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
private Content lead;
@ManyToMany
private Set<Content> popular;
@ManyToMany
private Set<Content> myList;
@ManyToMany
private Set<Content> recommended;
public ContentCollectionDTO toDTO() {
return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended));
}
private List<ContentDTO> asDTO(Set<Content> cnt) {
Source Listing - ContentCollection
codenameone.com github.com/codenameone/CodenameOne
Again it uses a UUID as an identifier. At the moment we’ll only have one content collection but in theory there can be as many as there are users.
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ContentCollection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
private Content lead;
@ManyToMany
private Set<Content> popular;
@ManyToMany
private Set<Content> myList;
@ManyToMany
private Set<Content> recommended;
public ContentCollectionDTO toDTO() {
return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended));
}
private List<ContentDTO> asDTO(Set<Content> cnt) {
Source Listing - ContentCollection
codenameone.com github.com/codenameone/CodenameOne
The lead content represents the show that appears on the top of the UI. In this case it’s the stranger things header
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ContentCollection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
private Content lead;
@ManyToMany
private Set<Content> popular;
@ManyToMany
private Set<Content> myList;
@ManyToMany
private Set<Content> recommended;
public ContentCollectionDTO toDTO() {
return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended));
}
private List<ContentDTO> asDTO(Set<Content> cnt) {
Source Listing - ContentCollection
codenameone.com github.com/codenameone/CodenameOne
These represent the rows of content below that. They contain the popular, recommended and personal list of shows.
public class ContentCollection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
private Content lead;
@ManyToMany
private Set<Content> popular;
@ManyToMany
private Set<Content> myList;
@ManyToMany
private Set<Content> recommended;
public ContentCollectionDTO toDTO() {
return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended));
}
private List<ContentDTO> asDTO(Set<Content> cnt) {
return cnt.stream().
map(Content::getDTO).
collect(Collectors.toList());
}
}
Source Listing - ContentCollection
codenameone.com github.com/codenameone/CodenameOne
Next we have the method that returns the DTO. Since all lists are effectively lists of content we use the same method to convert everything
public class ContentCollection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
@ManyToOne
private Content lead;
@ManyToMany
private Set<Content> popular;
@ManyToMany
private Set<Content> myList;
@ManyToMany
private Set<Content> recommended;
public ContentCollectionDTO toDTO() {
return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended));
}
private List<ContentDTO> asDTO(Set<Content> cnt) {
return cnt.stream().
map(Content::getDTO).
collect(Collectors.toList());
}
}
Source Listing - ContentCollection
codenameone.com github.com/codenameone/CodenameOne
Again we make use of Java 8 streams to get all the DTOs from the list by invoking the getDTO method on every element.
@RequestMapping("/video")
@RestController
@AllArgsConstructor
public class VideoWebService {
private final VideoService videoService;
@GetMapping("/list")
public ContentCollectionDTO getContent() {
return videoService.getContent();
}
}
Source Listing - VideoWebService
codenameone.com github.com/codenameone/CodenameOne
Now we’re getting to the web service code. We’re using the request mapping attribute to specify that this is a webservice on the video path. The RestController attribute
designates this as a simple JSON API so a lot of common sense defaults follow e.g. response in the body etc. 

Notice the all args constructor. This means the class has a constructor that accepts all the arguments. No default constructor. This is important!
@RequestMapping("/video")
@RestController
@AllArgsConstructor
public class VideoWebService {
private final VideoService videoService;
@GetMapping("/list")
public ContentCollectionDTO getContent() {
return videoService.getContent();
}
}
Source Listing - VideoWebService
codenameone.com github.com/codenameone/CodenameOne
Notice the final field for the video service. It’s passed via the constructor. Notice that the video service doesn’t have the autowired annotation we usually place for beans
in Spring Boot. This is called constructor injection and it has a few advantages. Normally it’s a bit too verbose as we need to maintain a constructor with all the injected
beans. But in this case Lombok makes it seamless.

In other words, Lombok and Spring boot inject the videoService bean pretty seamlessly for us.
@RequestMapping("/video")
@RestController
@AllArgsConstructor
public class VideoWebService {
private final VideoService videoService;
@GetMapping("/list")
public ContentCollectionDTO getContent() {
return videoService.getContent();
}
}
Source Listing - VideoWebService
codenameone.com github.com/codenameone/CodenameOne
We only have one API in the server, it returns the content JSON. A more real world API would also have authentication/identity APIs and maybe a state querying/
submitting API (view position, statistics etc.). But those are relatively simple and were covered by other modules here so I’m skipping them now.
@Service
@RequiredArgsConstructor
@Transactional
public class VideoService {
private final ContentRepository contentRepository;
private final ContentCollectionRepository contentCollectionRepository;
private final MediaRepository mediaRepository;
private final ResourceLoader resourceLoader;
private byte[] getResourceAsBytes(String res) throws IOException {
final Resource fileResource = resourceLoader.getResource("classpath:" + res);
@Cleanup InputStream is = fileResource.getInputStream();
return IOUtils.toByteArray(is);
}
@PostConstruct
public void initDB() throws IOException {
if(contentRepository.count() == 0) {
byte[] heroImage = getResourceAsBytes("images/hero-background.jpg");
Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(),
heroImage.length, heroImage, null, VideoQuality.NONE);
List<Media> thumbs = new ArrayList<>();
for (int iter = 1; iter < 9; iter++) {
byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg");
Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(),
showIcon.length, showIcon, null, VideoQuality.NONE);
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
The service class is similar to the rest API class. I used the RequiredArgsConstructor which is effectively the same as AllArgsConstructor in this case. It creates a
constructor for all the required args, specifically all the final fields. This again works for creating constructor based injection.

This class is also transactional as it accesses the database.
@Service
@RequiredArgsConstructor
@Transactional
public class VideoService {
private final ContentRepository contentRepository;
private final ContentCollectionRepository contentCollectionRepository;
private final MediaRepository mediaRepository;
private final ResourceLoader resourceLoader;
private byte[] getResourceAsBytes(String res) throws IOException {
final Resource fileResource = resourceLoader.getResource("classpath:" + res);
@Cleanup InputStream is = fileResource.getInputStream();
return IOUtils.toByteArray(is);
}
@PostConstruct
public void initDB() throws IOException {
if(contentRepository.count() == 0) {
byte[] heroImage = getResourceAsBytes("images/hero-background.jpg");
Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(),
heroImage.length, heroImage, null, VideoQuality.NONE);
List<Media> thumbs = new ArrayList<>();
for (int iter = 1; iter < 9; iter++) {
byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg");
Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(),
showIcon.length, showIcon, null, VideoQuality.NONE);
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
We need access to all the repositories to create the entities.
@Service
@RequiredArgsConstructor
@Transactional
public class VideoService {
private final ContentRepository contentRepository;
private final ContentCollectionRepository contentCollectionRepository;
private final MediaRepository mediaRepository;
private final ResourceLoader resourceLoader;
private byte[] getResourceAsBytes(String res) throws IOException {
final Resource fileResource = resourceLoader.getResource("classpath:" + res);
@Cleanup InputStream is = fileResource.getInputStream();
return IOUtils.toByteArray(is);
}
@PostConstruct
public void initDB() throws IOException {
if(contentRepository.count() == 0) {
byte[] heroImage = getResourceAsBytes("images/hero-background.jpg");
Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(),
heroImage.length, heroImage, null, VideoQuality.NONE);
List<Media> thumbs = new ArrayList<>();
for (int iter = 1; iter < 9; iter++) {
byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg");
Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(),
showIcon.length, showIcon, null, VideoQuality.NONE);
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
This is a simple utility method to read bytes from a stream in the classpath. Notice the usage of the @Cleanup annotation from Lombok and Apaches IOUtils API.
private byte[] getResourceAsBytes(String res) throws IOException {
final Resource fileResource = resourceLoader.getResource("classpath:" + res);
@Cleanup InputStream is = fileResource.getInputStream();
return IOUtils.toByteArray(is);
}
@PostConstruct
public void initDB() throws IOException {
if(contentRepository.count() == 0) {
byte[] heroImage = getResourceAsBytes("images/hero-background.jpg");
Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(),
heroImage.length, heroImage, null, VideoQuality.NONE);
List<Media> thumbs = new ArrayList<>();
for (int iter = 1; iter < 9; iter++) {
byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg");
Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(),
showIcon.length, showIcon, null, VideoQuality.NONE);
thumbs.add(thumb);
}
byte[] showLogo = getResourceAsBytes("images/show-logo.png");
Media strangerThingsLogoImage = new Media(null, "show-logo.png", "image/png", Instant.now(),
showLogo.length, showLogo, null, VideoQuality.NONE);
Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4",
VideoQuality.LOW);
Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
@PostConstruct is a feature of Spring boot that lets us invoke a method after the container was constructed. This is effectively a constructor for the entire application.
Here we can initialize the app with default data if necessary. Notice I throw an exception here since I assume this method won’t fail. It’s the first launch so it’s core that it
succeeds.
final Resource fileResource = resourceLoader.getResource("classpath:" + res);
@Cleanup InputStream is = fileResource.getInputStream();
return IOUtils.toByteArray(is);
}
@PostConstruct
public void initDB() throws IOException {
if(contentRepository.count() == 0) {
byte[] heroImage = getResourceAsBytes("images/hero-background.jpg");
Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(),
heroImage.length, heroImage, null, VideoQuality.NONE);
List<Media> thumbs = new ArrayList<>();
for (int iter = 1; iter < 9; iter++) {
byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg");
Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(),
showIcon.length, showIcon, null, VideoQuality.NONE);
thumbs.add(thumb);
}
byte[] showLogo = getResourceAsBytes("images/show-logo.png");
Media strangerThingsLogoImage = new Media(null, "show-logo.png", "image/png", Instant.now(),
showLogo.length, showLogo, null, VideoQuality.NONE);
Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4",
VideoQuality.LOW);
Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_640_3MG.mp4",
VideoQuality.MEDIUM);
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
It’s pretty easy to detect the first launch, if the database is empty the count method on the repository will return zero elements. In that case we need to initialize the
database.
@Cleanup InputStream is = fileResource.getInputStream();
return IOUtils.toByteArray(is);
}
@PostConstruct
public void initDB() throws IOException {
if(contentRepository.count() == 0) {
byte[] heroImage = getResourceAsBytes("images/hero-background.jpg");
Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(),
heroImage.length, heroImage, null, VideoQuality.NONE);
List<Media> thumbs = new ArrayList<>();
for (int iter = 1; iter < 9; iter++) {
byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg");
Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(),
showIcon.length, showIcon, null, VideoQuality.NONE);
thumbs.add(thumb);
}
byte[] showLogo = getResourceAsBytes("images/show-logo.png");
Media strangerThingsLogoImage = new Media(null, "show-logo.png", "image/png", Instant.now(),
showLogo.length, showLogo, null, VideoQuality.NONE);
Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4",
VideoQuality.LOW);
Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_640_3MG.mp4",
VideoQuality.MEDIUM);
Media highQualityVideo = new Media(null, "high-quality-video.mp4", "video/mp4",
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
In this large method I setup the initial data in the database. I preferred doing it through code rather than manually populating the database and providing a pre-filled one
as it’s easier to do when working in a fluid environment where you constantly wipe the database.

In this case I just take the hardcoded images and get their byte array data. I then create media objects for all the thumbnail entities. The rest is pretty self explanatory.
Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4",
VideoQuality.LOW);
Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_640_3MG.mp4",
VideoQuality.MEDIUM);
Media highQualityVideo = new Media(null, "high-quality-video.mp4", "video/mp4",
Instant.now(), -1, null,
"https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_1280_10MG.mp4",
VideoQuality.HIGH);
Set<Media> videos = new HashSet<>();
videos.add(lowQualityVideo);
videos.add(mediumQualityVideo);
videos.add(highQualityVideo);
List<Media> allMedia = new ArrayList<>(thumbs);
allMedia.addAll(Arrays.asList(strangerThingsHeroImage, strangerThingsLogoImage, lowQualityVideo,
mediumQualityVideo, highQualityVideo));
mediaRepository.saveAll(allMedia);
List<Content> allContent = new ArrayList<>();
Content lead = new Content(null,
"Stranger Things",
"Stranger things description",
strangerThingsHeroImage,
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
Eventually all the videos are created and all the media entities are added. Notice that the URLs are to an external video sample site I was able to find online. 

This is consistent with the way a video site would work. Your actual content would be hosted on a CDN for performance. Also notice I didn’t get into the whole process of
encryption and complex DRM streaming. That’s a whole different level of complexity.
for (int iter = 0; iter < thumbs.size(); iter++) {
allContent.add(new Content(
null,
"Show " + iter,
"Lorem ipsum",
strangerThingsHeroImage,
thumbs.get(iter),
null,
videos));
}
Set<Content> popular = new HashSet<>(allContent.subList(0, 4));
Set<Content> myList = new HashSet<>(allContent.subList(4, 8));
Set<Content> recommended = new HashSet<>(allContent.subList(2, 6));
contentRepository.saveAll(allContent);
ContentCollection contentCollection = new ContentCollection(null, lead, popular, myList, recommended);
contentCollectionRepository.save(contentCollection);
}
}
public ContentCollectionDTO getContent() {
return contentCollectionRepository.findAll().get(0).toDTO();
}
}
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
Finally the last bit of content is added to the content repository and everything is saved to the database…
for (int iter = 0; iter < thumbs.size(); iter++) {
allContent.add(new Content(
null,
"Show " + iter,
"Lorem ipsum",
strangerThingsHeroImage,
thumbs.get(iter),
null,
videos));
}
Set<Content> popular = new HashSet<>(allContent.subList(0, 4));
Set<Content> myList = new HashSet<>(allContent.subList(4, 8));
Set<Content> recommended = new HashSet<>(allContent.subList(2, 6));
contentRepository.saveAll(allContent);
ContentCollection contentCollection = new ContentCollection(null, lead, popular, myList, recommended);
contentCollectionRepository.save(contentCollection);
}
}
public ContentCollectionDTO getContent() {
return contentCollectionRepository.findAll().get(0).toDTO();
}
}
Source Listing - VideoService
codenameone.com github.com/codenameone/CodenameOne
This is the entire server API. This returns a JSON structure used in the client. We could stream this in smaller blocks but that was already covered in the Facebook demo
so I skipped it here.
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-04-server-continued_transcript.pdf

Mastering the Sling Rewriter by Justin Edelson
Mastering the Sling Rewriter by Justin EdelsonMastering the Sling Rewriter by Justin Edelson
Mastering the Sling Rewriter by Justin Edelson
AEM HUB
 
Mastering the Sling Rewriter
Mastering the Sling RewriterMastering the Sling Rewriter
Mastering the Sling Rewriter
Justin Edelson
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android InfrastructureC.T.Co
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android InfrastructureAlexey Buzdin
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
javatwo2011
 
The Full Power of ASP.NET Web API
The Full Power of ASP.NET Web APIThe Full Power of ASP.NET Web API
The Full Power of ASP.NET Web API
Eyal Vardi
 
A brief overview of java frameworks
A brief overview of java frameworksA brief overview of java frameworks
A brief overview of java frameworks
MD Sayem Ahmed
 
Android application architecture
Android application architectureAndroid application architecture
Android application architecture
Romain Rochegude
 
Inversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionInversion of Control and Dependency Injection
Inversion of Control and Dependency Injection
Dinesh Sharma
 
Recyclerview in action
Recyclerview in action Recyclerview in action
Recyclerview in action
Pratama Nur Wijaya
 
Kotlin talk
Kotlin talkKotlin talk
Kotlin talk
Klemen Kresnik
 
Developing application for Windows Phone 7 in TDD
Developing application for Windows Phone 7 in TDDDeveloping application for Windows Phone 7 in TDD
Developing application for Windows Phone 7 in TDD
Michele Capra
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
ShaiAlmog1
 
Cloud native programming model comparison
Cloud native programming model comparisonCloud native programming model comparison
Cloud native programming model comparison
Emily Jiang
 
Angular Schematics
Angular SchematicsAngular Schematics
Angular Schematics
Christoffer Noring
 
1 MVC – Ajax and Modal Views AJAX stands for Asynch.docx
1  MVC – Ajax and Modal Views AJAX stands for Asynch.docx1  MVC – Ajax and Modal Views AJAX stands for Asynch.docx
1 MVC – Ajax and Modal Views AJAX stands for Asynch.docx
honey725342
 
2012: ql.io and Node.js
2012: ql.io and Node.js2012: ql.io and Node.js
2012: ql.io and Node.js
Jonathan LeBlanc
 
APPlause - DemoCamp Munich
APPlause - DemoCamp MunichAPPlause - DemoCamp Munich
APPlause - DemoCamp Munich
Peter Friese
 
Intake 37 ef2
Intake 37 ef2Intake 37 ef2
Intake 37 ef2
Mahmoud Ouf
 

Similar to create-netflix-clone-04-server-continued_transcript.pdf (20)

Mastering the Sling Rewriter by Justin Edelson
Mastering the Sling Rewriter by Justin EdelsonMastering the Sling Rewriter by Justin Edelson
Mastering the Sling Rewriter by Justin Edelson
 
Mastering the Sling Rewriter
Mastering the Sling RewriterMastering the Sling Rewriter
Mastering the Sling Rewriter
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
The Full Power of ASP.NET Web API
The Full Power of ASP.NET Web APIThe Full Power of ASP.NET Web API
The Full Power of ASP.NET Web API
 
A brief overview of java frameworks
A brief overview of java frameworksA brief overview of java frameworks
A brief overview of java frameworks
 
Android application architecture
Android application architectureAndroid application architecture
Android application architecture
 
Inversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionInversion of Control and Dependency Injection
Inversion of Control and Dependency Injection
 
Recyclerview in action
Recyclerview in action Recyclerview in action
Recyclerview in action
 
Kotlin talk
Kotlin talkKotlin talk
Kotlin talk
 
Developing application for Windows Phone 7 in TDD
Developing application for Windows Phone 7 in TDDDeveloping application for Windows Phone 7 in TDD
Developing application for Windows Phone 7 in TDD
 
Working with Servlets
Working with ServletsWorking with Servlets
Working with Servlets
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
Cloud native programming model comparison
Cloud native programming model comparisonCloud native programming model comparison
Cloud native programming model comparison
 
Angular Schematics
Angular SchematicsAngular Schematics
Angular Schematics
 
1 MVC – Ajax and Modal Views AJAX stands for Asynch.docx
1  MVC – Ajax and Modal Views AJAX stands for Asynch.docx1  MVC – Ajax and Modal Views AJAX stands for Asynch.docx
1 MVC – Ajax and Modal Views AJAX stands for Asynch.docx
 
2012: ql.io and Node.js
2012: ql.io and Node.js2012: ql.io and Node.js
2012: ql.io and Node.js
 
APPlause - DemoCamp Munich
APPlause - DemoCamp MunichAPPlause - DemoCamp Munich
APPlause - DemoCamp Munich
 
Intake 37 ef2
Intake 37 ef2Intake 37 ef2
Intake 37 ef2
 

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.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
ShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
Creating a Whatsapp Clone - Part III - Transcript.pdf
Creating a Whatsapp Clone - Part III - Transcript.pdfCreating a Whatsapp Clone - Part III - Transcript.pdf
Creating a Whatsapp Clone - Part III - Transcript.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdfCreating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdf
ShaiAlmog1
 

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-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_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-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
 
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 I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.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
 
Creating a Whatsapp Clone - Part III - Transcript.pdf
Creating a Whatsapp Clone - Part III - Transcript.pdfCreating a Whatsapp Clone - Part III - Transcript.pdf
Creating a Whatsapp Clone - Part III - Transcript.pdf
 
Creating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdfCreating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdf
 

Recently uploaded

Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
Thijs Feryn
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Jeffrey Haguewood
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
Ralf Eggert
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
Product School
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 

Recently uploaded (20)

Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 

create-netflix-clone-04-server-continued_transcript.pdf

  • 1. Creating a Netflix Clone IV In this final instalment covering the server we’ll go over the service classes and the final entity representing the content creation. The content collection is an over simplification of the concept. A site like Netflix would probably generate this data dynamically based on user viewing preferences and complex heuristics. I just hardcoded an entity which was simpler.
  • 2. codenameone.com github.com/codenameone/CodenameOne Server Continued In this final instalment covering the server we’ll go over the service classes and the final entity representing the content creation. The content collection is an over simplification of the concept. A site like Netflix would probably generate this data dynamically based on user viewing preferences and complex heuristics. I just hardcoded an entity which was simpler.
  • 3. codenameone.com github.com/codenameone/CodenameOne Server Continued We have one last entity to cover, the ContentCollection Then we cover the service class In this final instalment covering the server we’ll go over the service classes and the final entity representing the content creation. The content collection is an over simplification of the concept. A site like Netflix would probably generate this data dynamically based on user viewing preferences and complex heuristics. I just hardcoded an entity which was simpler. The service class doesn’t do anything other than create the builtin data and implements the basic service call.
  • 4. @Entity @Data @AllArgsConstructor @NoArgsConstructor public class ContentCollection { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @ManyToOne private Content lead; @ManyToMany private Set<Content> popular; @ManyToMany private Set<Content> myList; @ManyToMany private Set<Content> recommended; public ContentCollectionDTO toDTO() { return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended)); } private List<ContentDTO> asDTO(Set<Content> cnt) { Source Listing - ContentCollection codenameone.com github.com/codenameone/CodenameOne The content collection starts similarly to the other entities
  • 5. @Entity @Data @AllArgsConstructor @NoArgsConstructor public class ContentCollection { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @ManyToOne private Content lead; @ManyToMany private Set<Content> popular; @ManyToMany private Set<Content> myList; @ManyToMany private Set<Content> recommended; public ContentCollectionDTO toDTO() { return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended)); } private List<ContentDTO> asDTO(Set<Content> cnt) { Source Listing - ContentCollection codenameone.com github.com/codenameone/CodenameOne Again it uses a UUID as an identifier. At the moment we’ll only have one content collection but in theory there can be as many as there are users.
  • 6. @Entity @Data @AllArgsConstructor @NoArgsConstructor public class ContentCollection { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @ManyToOne private Content lead; @ManyToMany private Set<Content> popular; @ManyToMany private Set<Content> myList; @ManyToMany private Set<Content> recommended; public ContentCollectionDTO toDTO() { return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended)); } private List<ContentDTO> asDTO(Set<Content> cnt) { Source Listing - ContentCollection codenameone.com github.com/codenameone/CodenameOne The lead content represents the show that appears on the top of the UI. In this case it’s the stranger things header
  • 7. @Entity @Data @AllArgsConstructor @NoArgsConstructor public class ContentCollection { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @ManyToOne private Content lead; @ManyToMany private Set<Content> popular; @ManyToMany private Set<Content> myList; @ManyToMany private Set<Content> recommended; public ContentCollectionDTO toDTO() { return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended)); } private List<ContentDTO> asDTO(Set<Content> cnt) { Source Listing - ContentCollection codenameone.com github.com/codenameone/CodenameOne These represent the rows of content below that. They contain the popular, recommended and personal list of shows.
  • 8. public class ContentCollection { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @ManyToOne private Content lead; @ManyToMany private Set<Content> popular; @ManyToMany private Set<Content> myList; @ManyToMany private Set<Content> recommended; public ContentCollectionDTO toDTO() { return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended)); } private List<ContentDTO> asDTO(Set<Content> cnt) { return cnt.stream(). map(Content::getDTO). collect(Collectors.toList()); } } Source Listing - ContentCollection codenameone.com github.com/codenameone/CodenameOne Next we have the method that returns the DTO. Since all lists are effectively lists of content we use the same method to convert everything
  • 9. public class ContentCollection { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @ManyToOne private Content lead; @ManyToMany private Set<Content> popular; @ManyToMany private Set<Content> myList; @ManyToMany private Set<Content> recommended; public ContentCollectionDTO toDTO() { return new ContentCollectionDTO(lead.getDTO(), asDTO(popular), asDTO(myList), asDTO(recommended)); } private List<ContentDTO> asDTO(Set<Content> cnt) { return cnt.stream(). map(Content::getDTO). collect(Collectors.toList()); } } Source Listing - ContentCollection codenameone.com github.com/codenameone/CodenameOne Again we make use of Java 8 streams to get all the DTOs from the list by invoking the getDTO method on every element.
  • 10. @RequestMapping("/video") @RestController @AllArgsConstructor public class VideoWebService { private final VideoService videoService; @GetMapping("/list") public ContentCollectionDTO getContent() { return videoService.getContent(); } } Source Listing - VideoWebService codenameone.com github.com/codenameone/CodenameOne Now we’re getting to the web service code. We’re using the request mapping attribute to specify that this is a webservice on the video path. The RestController attribute designates this as a simple JSON API so a lot of common sense defaults follow e.g. response in the body etc. Notice the all args constructor. This means the class has a constructor that accepts all the arguments. No default constructor. This is important!
  • 11. @RequestMapping("/video") @RestController @AllArgsConstructor public class VideoWebService { private final VideoService videoService; @GetMapping("/list") public ContentCollectionDTO getContent() { return videoService.getContent(); } } Source Listing - VideoWebService codenameone.com github.com/codenameone/CodenameOne Notice the final field for the video service. It’s passed via the constructor. Notice that the video service doesn’t have the autowired annotation we usually place for beans in Spring Boot. This is called constructor injection and it has a few advantages. Normally it’s a bit too verbose as we need to maintain a constructor with all the injected beans. But in this case Lombok makes it seamless. In other words, Lombok and Spring boot inject the videoService bean pretty seamlessly for us.
  • 12. @RequestMapping("/video") @RestController @AllArgsConstructor public class VideoWebService { private final VideoService videoService; @GetMapping("/list") public ContentCollectionDTO getContent() { return videoService.getContent(); } } Source Listing - VideoWebService codenameone.com github.com/codenameone/CodenameOne We only have one API in the server, it returns the content JSON. A more real world API would also have authentication/identity APIs and maybe a state querying/ submitting API (view position, statistics etc.). But those are relatively simple and were covered by other modules here so I’m skipping them now.
  • 13. @Service @RequiredArgsConstructor @Transactional public class VideoService { private final ContentRepository contentRepository; private final ContentCollectionRepository contentCollectionRepository; private final MediaRepository mediaRepository; private final ResourceLoader resourceLoader; private byte[] getResourceAsBytes(String res) throws IOException { final Resource fileResource = resourceLoader.getResource("classpath:" + res); @Cleanup InputStream is = fileResource.getInputStream(); return IOUtils.toByteArray(is); } @PostConstruct public void initDB() throws IOException { if(contentRepository.count() == 0) { byte[] heroImage = getResourceAsBytes("images/hero-background.jpg"); Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(), heroImage.length, heroImage, null, VideoQuality.NONE); List<Media> thumbs = new ArrayList<>(); for (int iter = 1; iter < 9; iter++) { byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg"); Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(), showIcon.length, showIcon, null, VideoQuality.NONE); Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne The service class is similar to the rest API class. I used the RequiredArgsConstructor which is effectively the same as AllArgsConstructor in this case. It creates a constructor for all the required args, specifically all the final fields. This again works for creating constructor based injection. This class is also transactional as it accesses the database.
  • 14. @Service @RequiredArgsConstructor @Transactional public class VideoService { private final ContentRepository contentRepository; private final ContentCollectionRepository contentCollectionRepository; private final MediaRepository mediaRepository; private final ResourceLoader resourceLoader; private byte[] getResourceAsBytes(String res) throws IOException { final Resource fileResource = resourceLoader.getResource("classpath:" + res); @Cleanup InputStream is = fileResource.getInputStream(); return IOUtils.toByteArray(is); } @PostConstruct public void initDB() throws IOException { if(contentRepository.count() == 0) { byte[] heroImage = getResourceAsBytes("images/hero-background.jpg"); Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(), heroImage.length, heroImage, null, VideoQuality.NONE); List<Media> thumbs = new ArrayList<>(); for (int iter = 1; iter < 9; iter++) { byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg"); Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(), showIcon.length, showIcon, null, VideoQuality.NONE); Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne We need access to all the repositories to create the entities.
  • 15. @Service @RequiredArgsConstructor @Transactional public class VideoService { private final ContentRepository contentRepository; private final ContentCollectionRepository contentCollectionRepository; private final MediaRepository mediaRepository; private final ResourceLoader resourceLoader; private byte[] getResourceAsBytes(String res) throws IOException { final Resource fileResource = resourceLoader.getResource("classpath:" + res); @Cleanup InputStream is = fileResource.getInputStream(); return IOUtils.toByteArray(is); } @PostConstruct public void initDB() throws IOException { if(contentRepository.count() == 0) { byte[] heroImage = getResourceAsBytes("images/hero-background.jpg"); Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(), heroImage.length, heroImage, null, VideoQuality.NONE); List<Media> thumbs = new ArrayList<>(); for (int iter = 1; iter < 9; iter++) { byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg"); Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(), showIcon.length, showIcon, null, VideoQuality.NONE); Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne This is a simple utility method to read bytes from a stream in the classpath. Notice the usage of the @Cleanup annotation from Lombok and Apaches IOUtils API.
  • 16. private byte[] getResourceAsBytes(String res) throws IOException { final Resource fileResource = resourceLoader.getResource("classpath:" + res); @Cleanup InputStream is = fileResource.getInputStream(); return IOUtils.toByteArray(is); } @PostConstruct public void initDB() throws IOException { if(contentRepository.count() == 0) { byte[] heroImage = getResourceAsBytes("images/hero-background.jpg"); Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(), heroImage.length, heroImage, null, VideoQuality.NONE); List<Media> thumbs = new ArrayList<>(); for (int iter = 1; iter < 9; iter++) { byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg"); Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(), showIcon.length, showIcon, null, VideoQuality.NONE); thumbs.add(thumb); } byte[] showLogo = getResourceAsBytes("images/show-logo.png"); Media strangerThingsLogoImage = new Media(null, "show-logo.png", "image/png", Instant.now(), showLogo.length, showLogo, null, VideoQuality.NONE); Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4", VideoQuality.LOW); Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4", Instant.now(), -1, null, Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne @PostConstruct is a feature of Spring boot that lets us invoke a method after the container was constructed. This is effectively a constructor for the entire application. Here we can initialize the app with default data if necessary. Notice I throw an exception here since I assume this method won’t fail. It’s the first launch so it’s core that it succeeds.
  • 17. final Resource fileResource = resourceLoader.getResource("classpath:" + res); @Cleanup InputStream is = fileResource.getInputStream(); return IOUtils.toByteArray(is); } @PostConstruct public void initDB() throws IOException { if(contentRepository.count() == 0) { byte[] heroImage = getResourceAsBytes("images/hero-background.jpg"); Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(), heroImage.length, heroImage, null, VideoQuality.NONE); List<Media> thumbs = new ArrayList<>(); for (int iter = 1; iter < 9; iter++) { byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg"); Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(), showIcon.length, showIcon, null, VideoQuality.NONE); thumbs.add(thumb); } byte[] showLogo = getResourceAsBytes("images/show-logo.png"); Media strangerThingsLogoImage = new Media(null, "show-logo.png", "image/png", Instant.now(), showLogo.length, showLogo, null, VideoQuality.NONE); Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4", VideoQuality.LOW); Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_640_3MG.mp4", VideoQuality.MEDIUM); Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne It’s pretty easy to detect the first launch, if the database is empty the count method on the repository will return zero elements. In that case we need to initialize the database.
  • 18. @Cleanup InputStream is = fileResource.getInputStream(); return IOUtils.toByteArray(is); } @PostConstruct public void initDB() throws IOException { if(contentRepository.count() == 0) { byte[] heroImage = getResourceAsBytes("images/hero-background.jpg"); Media strangerThingsHeroImage = new Media(null, "hero-background.jpg", "image/jpeg", Instant.now(), heroImage.length, heroImage, null, VideoQuality.NONE); List<Media> thumbs = new ArrayList<>(); for (int iter = 1; iter < 9; iter++) { byte[] showIcon = getResourceAsBytes("images/thumb" + iter + ".jpg"); Media thumb = new Media(null, "thumb.jpg", "image/jpeg", Instant.now(), showIcon.length, showIcon, null, VideoQuality.NONE); thumbs.add(thumb); } byte[] showLogo = getResourceAsBytes("images/show-logo.png"); Media strangerThingsLogoImage = new Media(null, "show-logo.png", "image/png", Instant.now(), showLogo.length, showLogo, null, VideoQuality.NONE); Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4", VideoQuality.LOW); Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_640_3MG.mp4", VideoQuality.MEDIUM); Media highQualityVideo = new Media(null, "high-quality-video.mp4", "video/mp4", Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne In this large method I setup the initial data in the database. I preferred doing it through code rather than manually populating the database and providing a pre-filled one as it’s easier to do when working in a fluid environment where you constantly wipe the database. In this case I just take the hardcoded images and get their byte array data. I then create media objects for all the thumbnail entities. The rest is pretty self explanatory.
  • 19. Media lowQualityVideo = new Media(null, "low-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_480_1_5MG.mp4", VideoQuality.LOW); Media mediumQualityVideo = new Media(null, "medium-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_640_3MG.mp4", VideoQuality.MEDIUM); Media highQualityVideo = new Media(null, "high-quality-video.mp4", "video/mp4", Instant.now(), -1, null, "https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_1280_10MG.mp4", VideoQuality.HIGH); Set<Media> videos = new HashSet<>(); videos.add(lowQualityVideo); videos.add(mediumQualityVideo); videos.add(highQualityVideo); List<Media> allMedia = new ArrayList<>(thumbs); allMedia.addAll(Arrays.asList(strangerThingsHeroImage, strangerThingsLogoImage, lowQualityVideo, mediumQualityVideo, highQualityVideo)); mediaRepository.saveAll(allMedia); List<Content> allContent = new ArrayList<>(); Content lead = new Content(null, "Stranger Things", "Stranger things description", strangerThingsHeroImage, Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne Eventually all the videos are created and all the media entities are added. Notice that the URLs are to an external video sample site I was able to find online. This is consistent with the way a video site would work. Your actual content would be hosted on a CDN for performance. Also notice I didn’t get into the whole process of encryption and complex DRM streaming. That’s a whole different level of complexity.
  • 20. for (int iter = 0; iter < thumbs.size(); iter++) { allContent.add(new Content( null, "Show " + iter, "Lorem ipsum", strangerThingsHeroImage, thumbs.get(iter), null, videos)); } Set<Content> popular = new HashSet<>(allContent.subList(0, 4)); Set<Content> myList = new HashSet<>(allContent.subList(4, 8)); Set<Content> recommended = new HashSet<>(allContent.subList(2, 6)); contentRepository.saveAll(allContent); ContentCollection contentCollection = new ContentCollection(null, lead, popular, myList, recommended); contentCollectionRepository.save(contentCollection); } } public ContentCollectionDTO getContent() { return contentCollectionRepository.findAll().get(0).toDTO(); } } Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne Finally the last bit of content is added to the content repository and everything is saved to the database…
  • 21. for (int iter = 0; iter < thumbs.size(); iter++) { allContent.add(new Content( null, "Show " + iter, "Lorem ipsum", strangerThingsHeroImage, thumbs.get(iter), null, videos)); } Set<Content> popular = new HashSet<>(allContent.subList(0, 4)); Set<Content> myList = new HashSet<>(allContent.subList(4, 8)); Set<Content> recommended = new HashSet<>(allContent.subList(2, 6)); contentRepository.saveAll(allContent); ContentCollection contentCollection = new ContentCollection(null, lead, popular, myList, recommended); contentCollectionRepository.save(contentCollection); } } public ContentCollectionDTO getContent() { return contentCollectionRepository.findAll().get(0).toDTO(); } } Source Listing - VideoService codenameone.com github.com/codenameone/CodenameOne This is the entire server API. This returns a JSON structure used in the client. We could stream this in smaller blocks but that was already covered in the Facebook demo so I skipped it here.
  • 22. 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
  • 23. Thank You Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
  • 24. Thanks for watching I hope you’ll enjoy the rest of the course and find it educational