Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The State of Server-Side Java Webapps (Devoxx 2...

The State of Server-Side Java Webapps (Devoxx 2024)

So, your application needs a web interface. Looking at today’s trends, you might think JavaScript frameworks are reigning: React, Vue, Angular and many more. On the language axis, JavaScript got company from TypeScript, Web Assembly and even C#.

But it’s not all single-page applications that glitter! Of course, a single-page app delivers highly dynamic and interactive user interfaces, but at what expense? What if we revisited some of the ages past, when the server generated our web pages? Can we satisfy the ever-changing requirements of today’s users for more interaction and dynamic behaviours using these patterns?

Join me on a trip down memory lane so that we can appreciate server-side web applications again. We’ll investigate today’s options for keeping things simple. Of course, we’ll see the contestants in action. This doesn’t have to be boring, after all…

Maarten Mulders

October 10, 2024
Tweet

More Decks by Maarten Mulders

Other Decks in Technology

Transcript

  1. TODAY'S AGENDA 1. What's Wrong With Single-Page Apps? 2. Server-Side

    Rendering 101 3. Architectures 4. Implementations 5. But... #serverside #java #web Maarten Mulders (@mthmulders)
  2. User Browser Single-page app (REST) API Navigate to app Request

    index page Return almost empty HTML Render HTML Show almost empty page Return JavaScript bundle(s) Execute JavaScript Render user interface Interact with page Execute JavaScript Fetch data Return data Execute JavaScript Render updated user interface #serverside #java #web Maarten Mulders (@mthmulders)
  3. 1. INITIAL LOADING TIME Stack Over ow: 556.37 kB of

    initial HTML document 2.2 MB of JavaScript GitHub: 238.45 kB of initial HTML document another 821.25 kB of HTML documents 2.7 MB of JavaScript #serverside #java #web Maarten Mulders (@mthmulders)
  4. KEY METRICS FROM Stack Over ow GitHub First Contentful Paint

    sec 2.8 2.6 Total Blocking Time ms 340 3,150 Speed Index s 6.5 5.5 Largest Contentful Paint s 10.8 9.5 Cumulative Layout Shift 0.075 0 LIGHTHOUSE #serverside #java #web Maarten Mulders (@mthmulders)
  5. ⚠ INITIAL LOADING TIME CAN SEVERELY IMPACT THE USER EXPERIENCE

    #serverside #java #web Maarten Mulders (@mthmulders)
  6. 2. BAD FOR SEARCH ENGINES 2. BAD FOR SEARCH ENGINES

    2. BAD FOR SEARCH ENGINES 2. BAD FOR SEARCH ENGINES 2. BAD FOR SEARCH ENGINES Crawlers index the information they nd on the internet: 1. Navigate to a page 2. Attempt to extract structure and text ⚠ 3. Follow reference(s) to new page(s) 4. Repeat until eternity Picture: own work #serverside #java #web Maarten Mulders (@mthmulders)
  7. ⚠ HERE BE DRAGONS! 1. Initially, the spider receives an

    almost empty page 2. The spider must process (execute!) all the JavaScript 3. Finally, the spider can extract page structure and content Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  8. 3. HARD ACCESSIBILITY 3. HARD ACCESSIBILITY 3. HARD ACCESSIBILITY 3.

    HARD ACCESSIBILITY 3. HARD ACCESSIBILITY Visual impaired people can use braille displays and screen readers. Both rely heavily on structure and metadata of a page. Picture by - Sebastien Delorme CC BY-SA 3.0 #serverside #java #web Maarten Mulders (@mthmulders)
  9. ⚠ HERE BE DRAGONS! 1. Initially, the spider receives an

    almost empty page 2. The spider must process (execute!) all the JavaScript 3. Finally, the spider can extract page structure and content Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  10. 4. COMPLEX SECURITY 4. COMPLEX SECURITY 4. COMPLEX SECURITY 4.

    COMPLEX SECURITY 4. COMPLEX SECURITY Separating user interface (UI) and the data means Properly securing the data (d'oh!) ... and the UI Validating user input in the UI ... and once again in the API Not leaking API credentials in the JavaScript bundles Session management on client and server ... Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  11. 5. NEEDS FALLBACK 5. NEEDS FALLBACK 5. NEEDS FALLBACK 5.

    NEEDS FALLBACK 5. NEEDS FALLBACK But what... if the user doesn't allow JavaScript to run? Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  12. THE ANSWER Server-Side Rendering But this time with a fancier

    names, such as "pre-rendering" or "server hydration". #serverside #java #web Maarten Mulders (@mthmulders)
  13. User Browser Web app Navigate to app Request index page

    Return completely populated HTML Render HTML Show completely populated page Interact with page Request different page Return completely populated HTML Show completely populated page #serverside #java #web Maarten Mulders (@mthmulders)
  14. 1. SEARCH ENGINE OPTIMISATION 1. SEARCH ENGINE OPTIMISATION 1. SEARCH

    ENGINE OPTIMISATION 1. SEARCH ENGINE OPTIMISATION 1. SEARCH ENGINE OPTIMISATION Picture: own work #serverside #java #web Maarten Mulders (@mthmulders)
  15. 2. SIMPLE USER EXPERIENCE 2. SIMPLE USER EXPERIENCE 2. SIMPLE

    USER EXPERIENCE 2. SIMPLE USER EXPERIENCE 2. SIMPLE USER EXPERIENCE Because not every web application needs to be a Hollywood star Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  16. 3. TIME AND/OR BUDGET 3. TIME AND/OR BUDGET 3. TIME

    AND/OR BUDGET 3. TIME AND/OR BUDGET 3. TIME AND/OR BUDGET CONSTRAINTS CONSTRAINTS CONSTRAINTS CONSTRAINTS CONSTRAINTS ̎ PROOF-OF-CONCEPTS Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  17. ACTION-BASED (PUSH) ACTION-BASED (PUSH) ACTION-BASED (PUSH) ACTION-BASED (PUSH) ACTION-BASED (PUSH)

    Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  18. User/Browser Controller Model Request page Implemented by you! Retrieve opt

    Operate Select / Construct View Render Often delegated to a View Engine Read Rendered page #serverside #java #web Maarten Mulders (@mthmulders)
  19. POPULAR EXAMPLES Jakarta MVC Jakarta Server Pages, Jakarta Faces Spring

    MVC Jakarta Server Pages, Jakarta Faces, Thymeleaf Quarkus Qute Struts Freemarker, Velocity, Jakarta Server Pages #serverside #java #web Maarten Mulders (@mthmulders)
  20. COMPONENT-BASED (PULL) COMPONENT-BASED (PULL) COMPONENT-BASED (PULL) COMPONENT-BASED (PULL) COMPONENT-BASED (PULL)

    Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)
  21. User/Browser Controller Model Request view Provided by framework Select /

    Construct Component (View) Render Often delegated to a View Engine loop [Components in view] Render component Access opt Operate Rendered page #serverside #java #web Maarten Mulders (@mthmulders)
  22. CONTROLLER FOR SHOWING DATA @Path("/show") @Controller @RequestScoped public class ShowPollController

    { @Inject private Models models; @Inject private PollRepository pollRepository; @Inject private VoteSummaryService voteSummaryService; } #serverside #java #web Maarten Mulders (@mthmulders)
  23. CONTROLLER FOR SHOWING DATA (CTD) @GET @Path("/{slug}") @Produces("text/html; charset=UTF-8") public

    Response show(@PathParam("slug") String slug) { return pollRepository.f ndBySlug(slug) .map(this populateModelAndPrepareResponse) .orElse(Response.status(Response.Status.NOT_FOUND).build()); } #serverside #java #web Maarten Mulders (@mthmulders)
  24. CONTROLLER FOR SHOWING DATA (CTD) private Response populateModelAndPrepareResponse(f nal Poll

    poll) { models.put("poll", poll); models.put("votePercentages", voteSummaryService.calculateVotePercentages(poll)); models.put("voteCount", voteSummaryService.calculateVoteCount(poll)); return Response.ok("polls/show.jsp").build(); } #serverside #java #web Maarten Mulders (@mthmulders)
  25. VIEW FOR SHOWING DATA <layout:main title="Now voting #${poll.slug}"> <div class="flex

    flex row flex wrap"> <div class="basis full sm:basis-1/3 px-2"> <h2>${poll.question} h2> <% QR code omitted %> div> <div class="basis full sm:basis-2/3 px-2"> <% more details omitted %> div> div> layout:main> #serverside #java #web Maarten Mulders (@mthmulders)
  26. VIEW FOR PERFORMING AN ACTION <form method="post" action=""> <input type="hidden"

    name="${mvc.csrf.name}" value="${mvc.csrf.token}" Your choices: <c:forEach items="${poll.options}" var="option"> <div class="pt-4"> <input name="vote.selectedOption" value="${option.optionValue}" type="rad <label>${option.displayValue} label> div> c:forEach> <div class="pt-4"> <input type="submit" class="btn btn sm btn primary" value="Vote!" div> form> #serverside #java #web Maarten Mulders (@mthmulders)
  27. CONTROLLER FOR AN ACTION @Path("/vote") @Controller @RequestScoped public class VoteController

    { @Inject private Models models; @Inject private PollRepository pollRepository; @Inject private VotingService votingService; } #serverside #java #web Maarten Mulders (@mthmulders)
  28. CONTROLLER FOR AN ACTION (CTD) @CsrfProtected @POST @Path("/{slug}") @Produces("text/html; charset=UTF-8")

    @Transactional public Response processVote( @PathParam("slug") String slug, @FormParam("vote.selectedOption") Integer selectedOption) { return pollRepository.f ndBySlug(slug) .map(poll votingService .castVote(poll, ticketId, selectedOption) .map(vote prepareResponseAfterVote(poll, vote)) .orElse(Response.status(Response.Status.NOT_FOUND).build()); } #serverside #java #web Maarten Mulders (@mthmulders)
  29. CONTROLLER FOR AN ACTION (CTD) private Response prepareResponseAfterSuccessfulVote( f nal

    Poll poll, f nal Vote vote) { models.put("poll", poll); models.put("vote", vote); return Response.ok("polls/thanks.jsp").build(); } #serverside #java #web Maarten Mulders (@mthmulders)
  30. ACTION BASED: JAKARTA FACES With PrimeFaces (optional) as UI toolkit.

    #serverside #java #web Maarten Mulders (@mthmulders)
  31. COMPONENT FOR SHOWING DATA <ui:composition> <p:dataTable var="vehicle" value="${vehicleListView.vehicles}"> <p:column headerText="Code">

    <h:outputText value=" vehicle.code}" p:column> continued p:datatable> ui:composition> #serverside #java #web Maarten Mulders (@mthmulders)
  32. MODEL FOR SHOWING DATA @Named("vehicleListView") @ViewScoped public class VehicleListView {

    private Collection<VehicleDTO> vehicles; @Inject public VehicleListView(f nal VehicleRepository vehicleRepository) { this.vehicles = vehicleRepository.f ndAll() } public Collection<VehicleDTO> getVehicles() { return vehicles; } } #serverside #java #web Maarten Mulders (@mthmulders)
  33. OPERATING ON THE MODEL <p:commandButton icon="pi pi trash" class="ui button

    warning" oncomplete="PF('deleteVehicleDialog').show()" process="@this"> <f:setPropertyActionListener value=" vehicle}" target=" vehicleListView.selectedVehicle}" p:commandButton> <p:commandButton value="Yes" icon="pi pi check" actionListener=" vehicleListView.deleteVehicle}" process="@this" oncomplete="PF('deleteVehicleDialog').hide()" #serverside #java #web Maarten Mulders (@mthmulders)
  34. OPERATING ON THE MODEL public void deleteVehicle() { this.vehicleRepository.removeVehicle(selectedVehicle); var

    msg = new FacesMessage( "Vehicle %s removed".formatted(selectedVehicle.getCode())); FacesContext.getCurrentInstance().addMessage(null, msg); PrimeFaces.current().ajax().update("form:messages", "form:vehicles"); } #serverside #java #web Maarten Mulders (@mthmulders)
  35. I NEED A DYNAMIC UI/UX! I NEED A DYNAMIC UI/UX!

    I NEED A DYNAMIC UI/UX! I NEED A DYNAMIC UI/UX! I NEED A DYNAMIC UI/UX! Picture: - Action-based: look at , Component-based: look at the component framework, optionally add extra component libraries PxHere CC0 htmx alpine.js #serverside #java #web Maarten Mulders (@mthmulders)
  36. YOU USED TO ADVOCATE REACT?! YOU USED TO ADVOCATE REACT?!

    YOU USED TO ADVOCATE REACT?! YOU USED TO ADVOCATE REACT?! YOU USED TO ADVOCATE REACT?! Picture: screenshot from . Both SPA and SSR have their use cases, make a conscious choice! JFokus' YouTube channel #serverside #java #web Maarten Mulders (@mthmulders)
  37. WRAP UP WRAP UP WRAP UP WRAP UP WRAP UP

    #serverside #java #web Maarten Mulders (@mthmulders)
  38. WRAP UP WRAP UP WRAP UP WRAP UP WRAP UP

    1. X Challenge "we need a single-page app for this" 2. Architectures: push (action) vs. pull (component) 3. Implementations: Jakarta MVC, Jakarta Faces, Spring MVC, Quarkus 4. " Experiment with hybrid approaches: htmx, alpine.js ⭐ RATE THIS TALK IN THE DEVOXX APP! Picture: - PxHere CC0 #serverside #java #web Maarten Mulders (@mthmulders)