SlideShare a Scribd company logo
1 of 24
Download to read offline
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 EdelsonAEM HUB
 
Mastering the Sling Rewriter
Mastering the Sling RewriterMastering the Sling Rewriter
Mastering the Sling RewriterJustin Edelson
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android InfrastructureAlexey Buzdin
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android InfrastructureC.T.Co
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotationjavatwo2011
 
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 APIEyal Vardi
 
A brief overview of java frameworks
A brief overview of java frameworksA brief overview of java frameworks
A brief overview of java frameworksMD Sayem Ahmed
 
Android application architecture
Android application architectureAndroid application architecture
Android application architectureRomain Rochegude
 
Inversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionInversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionDinesh Sharma
 
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 TDDMichele Capra
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfShaiAlmog1
 
Cloud native programming model comparison
Cloud native programming model comparisonCloud native programming model comparison
Cloud native programming model comparisonEmily Jiang
 
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.docxhoney725342
 
APPlause - DemoCamp Munich
APPlause - DemoCamp MunichAPPlause - DemoCamp Munich
APPlause - DemoCamp MunichPeter Friese
 

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.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-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_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-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
 
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 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.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
 
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.pdfShaiAlmog1
 
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.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-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

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
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
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
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
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
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
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
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
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
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 

Recently uploaded (20)

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
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
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
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
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
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
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
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
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
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 

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