Taming Core Data by Arek Holko, MacoscopeMacoscope
Core Data is a framework that you use to manage the model layer objects in your application. A framework that you use to build the persistence layer in your application.
MBL301 Data Persistence to Amazon Dynamodb for Mobile Apps - AWS re: Invent 2012Amazon Web Services
Object modeling is a common practice in mobile applications. We present two methods for modeling objects backed by Amazon DynamoDB, the AWS Persistence Framework for Core Data and DynamoDBMapper. We cover the benefits and limitations of these two solutions and demonstrate sample applications built with both technologies.
Лаконічні та елегантні автоматизовані тести? Безболісний (майже) тестовий стек для UI веб-додатків? Більше часу, щоб випити кави/пограти у Cuphead/ подивитись у порожнечу?
Ми поговоримо про Lombok, Vavr, Owner, чому вам варто використовувати готові рішення та як зробити тести більш лаконічними та читабельними. Вам знадобиться Vaper та/або гіроборд.
Taming Core Data by Arek Holko, MacoscopeMacoscope
Core Data is a framework that you use to manage the model layer objects in your application. A framework that you use to build the persistence layer in your application.
MBL301 Data Persistence to Amazon Dynamodb for Mobile Apps - AWS re: Invent 2012Amazon Web Services
Object modeling is a common practice in mobile applications. We present two methods for modeling objects backed by Amazon DynamoDB, the AWS Persistence Framework for Core Data and DynamoDBMapper. We cover the benefits and limitations of these two solutions and demonstrate sample applications built with both technologies.
Лаконічні та елегантні автоматизовані тести? Безболісний (майже) тестовий стек для UI веб-додатків? Більше часу, щоб випити кави/пограти у Cuphead/ подивитись у порожнечу?
Ми поговоримо про Lombok, Vavr, Owner, чому вам варто використовувати готові рішення та як зробити тести більш лаконічними та читабельними. Вам знадобиться Vaper та/або гіроборд.
Developing application for Windows Phone 7 in TDDMichele Capra
A real example of how to develop an application for Windows Phone 7 with Test Driven Development approach. In this presentation you'll see also hoew to implements the Model-View-ViewModel (MVVM) pattern.
1 MVC – Ajax and Modal Views AJAX stands for Asynch.docxhoney725342
1
MVC – Ajax and Modal Views
AJAX stands for Asynchronous JavaScript and XML. It is a client-side capability that allows
silent updates of parts of a web page. Every browser provides a component called
XmlHttpRequest that is invoked via Javascript and has the capability to make asynchronous calls
back to the web server from where the page was obtained. One can set up Javascript timers to
automate the periodic retrieval of data using the XmlHttpRequest object.
Even though we can program the XmlHttpRequest object directly using Javascript, JQuery
makes it a lot easier to set it up and issue an asynchronous request to the web server. ASP.Net
MVC also provides a server-side solution to AJAX via the Ajax.BeginForm extension method.
We will demonstrate the two approaches to AJAX i.e., via JQuery and serializable partial views,
and via the Ajax.BeginForm.
Using SQL Server Management Studo, create a database called ProductsDB. Add a table called
Categories with the folling data.
The CategoryId column is of type int, and the CategoryName column is of type carchar(50).
Add another table called Products with the following design.
Then put the following data in the Products table.
2
Using Visual Studio, create a new project. Choose the web type of project, and select the MVC
template as shown below.
Add the following connection string information to the web.config file. Replace ALPHA with
the name of your database server.
<configuration>
<connectionStrings>
<add name="PRODUCTSDB" connectionString="server=ALPHA;integrated
security=true;database=ProductsDB" />
</connectionStrings>
<appSettings>
Add a class to the Models folder called Category with the following code in it.
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
3
Add a folder called DataLayer to the project. Then add an interface called IEntity to it with the
following code in it.
public interface IEntity
{ // any class that needs conversion to a List from a DataTable will implement this
void PopulateFields(DataRow dr);
}
Add a class called MyEntityBase to the Models folder with the following code in it. This class
uses reflection to populate the fields of a class from a given DataRow of a DataTable. All
repository methods that need to convert a DataTable to a list will indirectly use this method via
the generic DBList class..
public class MyEntityBase : IEntity
{
public void PopulateFields(DataRow dr)
{
// use reflection to populate the fields of this class from DataRow
Type tp = this.GetType();
foreach (PropertyInfo pi in tp.GetProperties())
pi.SetValue(this, dr[pi.Name]);
}
}
Add a class called DBList to the DataLayer folder with the following code in it.
class DBList
{
...
Developing application for Windows Phone 7 in TDDMichele Capra
A real example of how to develop an application for Windows Phone 7 with Test Driven Development approach. In this presentation you'll see also hoew to implements the Model-View-ViewModel (MVVM) pattern.
1 MVC – Ajax and Modal Views AJAX stands for Asynch.docxhoney725342
1
MVC – Ajax and Modal Views
AJAX stands for Asynchronous JavaScript and XML. It is a client-side capability that allows
silent updates of parts of a web page. Every browser provides a component called
XmlHttpRequest that is invoked via Javascript and has the capability to make asynchronous calls
back to the web server from where the page was obtained. One can set up Javascript timers to
automate the periodic retrieval of data using the XmlHttpRequest object.
Even though we can program the XmlHttpRequest object directly using Javascript, JQuery
makes it a lot easier to set it up and issue an asynchronous request to the web server. ASP.Net
MVC also provides a server-side solution to AJAX via the Ajax.BeginForm extension method.
We will demonstrate the two approaches to AJAX i.e., via JQuery and serializable partial views,
and via the Ajax.BeginForm.
Using SQL Server Management Studo, create a database called ProductsDB. Add a table called
Categories with the folling data.
The CategoryId column is of type int, and the CategoryName column is of type carchar(50).
Add another table called Products with the following design.
Then put the following data in the Products table.
2
Using Visual Studio, create a new project. Choose the web type of project, and select the MVC
template as shown below.
Add the following connection string information to the web.config file. Replace ALPHA with
the name of your database server.
<configuration>
<connectionStrings>
<add name="PRODUCTSDB" connectionString="server=ALPHA;integrated
security=true;database=ProductsDB" />
</connectionStrings>
<appSettings>
Add a class to the Models folder called Category with the following code in it.
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
3
Add a folder called DataLayer to the project. Then add an interface called IEntity to it with the
following code in it.
public interface IEntity
{ // any class that needs conversion to a List from a DataTable will implement this
void PopulateFields(DataRow dr);
}
Add a class called MyEntityBase to the Models folder with the following code in it. This class
uses reflection to populate the fields of a class from a given DataRow of a DataTable. All
repository methods that need to convert a DataTable to a list will indirectly use this method via
the generic DBList class..
public class MyEntityBase : IEntity
{
public void PopulateFields(DataRow dr)
{
// use reflection to populate the fields of this class from DataRow
Type tp = this.GetType();
foreach (PropertyInfo pi in tp.GetProperties())
pi.SetValue(this, dr[pi.Name]);
}
}
Add a class called DBList to the DataLayer folder with the following code in it.
class DBList
{
...
Neuro-symbolic is not enough, we need neuro-*semantic*Frank van Harmelen
Neuro-symbolic (NeSy) AI is on the rise. However, simply machine learning on just any symbolic structure is not sufficient to really harvest the gains of NeSy. These will only be gained when the symbolic structures have an actual semantics. I give an operational definition of semantics as “predictable inference”.
All of this illustrated with link prediction over knowledge graphs, but the argument is general.
Accelerate your Kubernetes clusters with Varnish CachingThijs Feryn
A presentation about the usage and availability of Varnish on Kubernetes. This talk explores the capabilities of Varnish caching and shows how to use the Varnish Helm chart to deploy it to Kubernetes.
This presentation was delivered at K8SUG Singapore. See https://feryn.eu/presentations/accelerate-your-kubernetes-clusters-with-varnish-caching-k8sug-singapore-28-2024 for more details.
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Jeffrey Haguewood
Sidekick Solutions uses Bonterra Impact Management (fka Social Solutions Apricot) and automation solutions to integrate data for business workflows.
We believe integration and automation are essential to user experience and the promise of efficient work through technology. Automation is the critical ingredient to realizing that full vision. We develop integration products and services for Bonterra Case Management software to support the deployment of automations for a variety of use cases.
This video focuses on the notifications, alerts, and approval requests using Slack for Bonterra Impact Management. The solutions covered in this webinar can also be deployed for Microsoft Teams.
Interested in deploying notification automations for Bonterra Impact Management? Contact us at sales@sidekicksolutionsllc.com to discuss next steps.
State of ICS and IoT Cyber Threat Landscape Report 2024 previewPrayukth K V
The IoT and OT threat landscape report has been prepared by the Threat Research Team at Sectrio using data from Sectrio, cyber threat intelligence farming facilities spread across over 85 cities around the world. In addition, Sectrio also runs AI-based advanced threat and payload engagement facilities that serve as sinks to attract and engage sophisticated threat actors, and newer malware including new variants and latent threats that are at an earlier stage of development.
The latest edition of the OT/ICS and IoT security Threat Landscape Report 2024 also covers:
State of global ICS asset and network exposure
Sectoral targets and attacks as well as the cost of ransom
Global APT activity, AI usage, actor and tactic profiles, and implications
Rise in volumes of AI-powered cyberattacks
Major cyber events in 2024
Malware and malicious payload trends
Cyberattack types and targets
Vulnerability exploit attempts on CVEs
Attacks on counties – USA
Expansion of bot farms – how, where, and why
In-depth analysis of the cyber threat landscape across North America, South America, Europe, APAC, and the Middle East
Why are attacks on smart factories rising?
Cyber risk predictions
Axis of attacks – Europe
Systemic attacks in the Middle East
Download the full report from here:
https://sectrio.com/resources/ot-threat-landscape-reports/sectrio-releases-ot-ics-and-iot-security-threat-landscape-report-2024/
Connector Corner: Automate dynamic content and events by pushing a buttonDianaGray10
Here is something new! In our next Connector Corner webinar, we will demonstrate how you can use a single workflow to:
Create a campaign using Mailchimp with merge tags/fields
Send an interactive Slack channel message (using buttons)
Have the message received by managers and peers along with a test email for review
But there’s more:
In a second workflow supporting the same use case, you’ll see:
Your campaign sent to target colleagues for approval
If the “Approve” button is clicked, a Jira/Zendesk ticket is created for the marketing design team
But—if the “Reject” button is pushed, colleagues will be alerted via Slack message
Join us to learn more about this new, human-in-the-loop capability, brought to you by Integration Service connectors.
And...
Speakers:
Akshay Agnihotri, Product Manager
Charlie Greenberg, Host
PHP Frameworks: I want to break free (IPC Berlin 2024)Ralf Eggert
In this presentation, we examine the challenges and limitations of relying too heavily on PHP frameworks in web development. We discuss the history of PHP and its frameworks to understand how this dependence has evolved. The focus will be on providing concrete tips and strategies to reduce reliance on these frameworks, based on real-world examples and practical considerations. The goal is to equip developers with the skills and knowledge to create more flexible and future-proof web applications. We'll explore the importance of maintaining autonomy in a rapidly changing tech landscape and how to make informed decisions in PHP development.
This talk is aimed at encouraging a more independent approach to using PHP frameworks, moving towards a more flexible and future-proof approach to PHP development.
Transcript: Selling digital books in 2024: Insights from industry leaders - T...BookNet Canada
The publishing industry has been selling digital audiobooks and ebooks for over a decade and has found its groove. What’s changed? What has stayed the same? Where do we go from here? Join a group of leading sales peers from across the industry for a conversation about the lessons learned since the popularization of digital books, best practices, digital book supply chain management, and more.
Link to video recording: https://bnctechforum.ca/sessions/selling-digital-books-in-2024-insights-from-industry-leaders/
Presented by BookNet Canada on May 28, 2024, with support from the Department of Canadian Heritage.
Essentials of Automations: Optimizing FME Workflows with ParametersSafe Software
Are you looking to streamline your workflows and boost your projects’ efficiency? Do you find yourself searching for ways to add flexibility and control over your FME workflows? If so, you’re in the right place.
Join us for an insightful dive into the world of FME parameters, a critical element in optimizing workflow efficiency. This webinar marks the beginning of our three-part “Essentials of Automation” series. This first webinar is designed to equip you with the knowledge and skills to utilize parameters effectively: enhancing the flexibility, maintainability, and user control of your FME projects.
Here’s what you’ll gain:
- Essentials of FME Parameters: Understand the pivotal role of parameters, including Reader/Writer, Transformer, User, and FME Flow categories. Discover how they are the key to unlocking automation and optimization within your workflows.
- Practical Applications in FME Form: Delve into key user parameter types including choice, connections, and file URLs. Allow users to control how a workflow runs, making your workflows more reusable. Learn to import values and deliver the best user experience for your workflows while enhancing accuracy.
- Optimization Strategies in FME Flow: Explore the creation and strategic deployment of parameters in FME Flow, including the use of deployment and geometry parameters, to maximize workflow efficiency.
- Pro Tips for Success: Gain insights on parameterizing connections and leveraging new features like Conditional Visibility for clarity and simplicity.
We’ll wrap up with a glimpse into future webinars, followed by a Q&A session to address your specific questions surrounding this topic.
Don’t miss this opportunity to elevate your FME expertise and drive your projects to new heights of efficiency.
Search and Society: Reimagining Information Access for Radical FuturesBhaskar Mitra
The field of Information retrieval (IR) is currently undergoing a transformative shift, at least partly due to the emerging applications of generative AI to information access. In this talk, we will deliberate on the sociotechnical implications of generative AI for information access. We will argue that there is both a critical necessity and an exciting opportunity for the IR community to re-center our research agendas on societal needs while dismantling the artificial separation between the work on fairness, accountability, transparency, and ethics in IR and the rest of IR research. Instead of adopting a reactionary strategy of trying to mitigate potential social harms from emerging technologies, the community should aim to proactively set the research agenda for the kinds of systems we should build inspired by diverse explicitly stated sociotechnical imaginaries. The sociotechnical imaginaries that underpin the design and development of information access technologies needs to be explicitly articulated, and we need to develop theories of change in context of these diverse perspectives. Our guiding future imaginaries must be informed by other academic fields, such as democratic theory and critical theory, and should be co-developed with social science scholars, legal scholars, civil rights and social justice activists, and artists, among others.
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.