<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sami Ekblad</title>
    <description>The latest articles on Forem by Sami Ekblad (@samiekblad).</description>
    <link>https://forem.com/samiekblad</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F74493%2F9f62615a-c322-4fbe-94c4-89f99b74affb.png</url>
      <title>Forem: Sami Ekblad</title>
      <link>https://forem.com/samiekblad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/samiekblad"/>
    <language>en</language>
    <item>
      <title>What makes Vaadin components special?</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Fri, 29 Aug 2025 09:06:00 +0000</pubDate>
      <link>https://forem.com/samiekblad/what-makes-vaadin-components-special-35ne</link>
      <guid>https://forem.com/samiekblad/what-makes-vaadin-components-special-35ne</guid>
      <description>&lt;p&gt;Vaadin has always been a bit different.&lt;/p&gt;

&lt;p&gt;Yes, it's a Java framework. Yes, it’s great for building modern web UIs. But if you’ve ever used Vaadin seriously, you’ve probably noticed something that sets it apart: its component architecture. &lt;/p&gt;

&lt;p&gt;Whether you're using a ready-made component from the official library, building your own from scratch, or integrating a 3rd-party NPM package, all Vaadin components follow a consistent maintainable structure that bridges the server and the client and can evolve along with your application. &lt;/p&gt;

&lt;h2&gt;
  
  
  Not just widgets
&lt;/h2&gt;

&lt;p&gt;There are lots of UI libraries out there. React has its components. Lit has web components. Bootstrap has... buttons.&lt;/p&gt;

&lt;p&gt;Vaadin? It’s not just about visuals. It is not uncommon that Vaadin application and components are developed and maintained for many years. That is by design and is part of the Vaadin ideology.&lt;/p&gt;

&lt;p&gt;With Vaadin the UI components can live entirely on the server, fully on the client, or anywhere in between. That flexibility is the superpower.&lt;/p&gt;

&lt;p&gt;To better understand what and how, let’s go through the main types of components Vaadin has and and you can also build with Vaadin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-only components
&lt;/h2&gt;

&lt;p&gt;This is the most "Vaadin-native" approach. You’re writing pure Java. No HTML templates. No JavaScript. Just composing UI dynamically from existing Vaadin components (like &lt;code&gt;TextField&lt;/code&gt;, &lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;Grid&lt;/code&gt;, etc.) and building higher-level features—think “InvoiceEditor” rather than just “Form”.&lt;/p&gt;

&lt;p&gt;Great for back-office UIs, internal tools, and anything where control and validation matter more than animations and visual finesse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f18y3acadhm7m8m8q33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f18y3acadhm7m8m8q33.png" alt="Vaadin Server Component Architecture" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full user input validation on the server&lt;/li&gt;
&lt;li&gt;No need to write or maintain any front-end code&lt;/li&gt;
&lt;li&gt;Dynamic UI generation on the fly&lt;/li&gt;
&lt;li&gt;Easy to hook into existing Java libraries and logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All interactions round-trip to the server&lt;/li&gt;
&lt;li&gt;Not ideal for things like animations or drag-and-drop interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Want to create your own? Use this &lt;a href="https://github.com/vaadin/addon-template" rel="noopener noreferrer"&gt;add-on project template&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Client-server components
&lt;/h2&gt;

&lt;p&gt;Here’s where things get exciting.&lt;/p&gt;

&lt;p&gt;This type of component combines a custom client-side element (written in Lit, TypeScript, or plain Web Components) with a Vaadin Java API on the server. You get the best of both worlds: &lt;strong&gt;smooth UX and secure logic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Want to build a date picker that works exactly how your users expect—right down to keyboard shortcuts and color transitions—but still ensure the selected date is validated against backend rules? This is your pattern.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz03g40pekelt7k43i2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz03g40pekelt7k43i2l.png" alt="Vaadin Client-Server Component Architecture" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full DOM access and rich interactions&lt;/li&gt;
&lt;li&gt;Server-side Java validation and logic&lt;/li&gt;
&lt;li&gt;Clean separation of concerns, with communication handled by the framework&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires knowledge of both client-side and server-side tech (TypeScript + Java)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Want to create your own? Use this &lt;a href="https://github.com/vaadin/client-server-addon-template" rel="noopener noreferrer"&gt;add-on project template&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping third-party custom elements
&lt;/h2&gt;

&lt;p&gt;Sometimes, you don’t want to build it yourself. Fair enough.&lt;/p&gt;

&lt;p&gt;In 2025, the web is full of amazing &lt;a href="https://www.npmjs.com/search?q=custom%20element" rel="noopener noreferrer"&gt;custom elements&lt;/a&gt; you can install with &lt;code&gt;npm&lt;/code&gt; and drop into your app. Vaadin makes it easy to wrap these into reusable components—with a little TypeScript glue—and expose them to your Java UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhnle0txlm1ijv3725zxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhnle0txlm1ijv3725zxu.png" alt="Vaadin NPM Component Architecture" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rapidly integrate NPM-based components into your Java app&lt;/li&gt;
&lt;li&gt;Minimal boilerplate to get started&lt;/li&gt;
&lt;li&gt;Great for visual enhancements or non-critical UX features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-side validation is still your responsibility&lt;/li&gt;
&lt;li&gt;Some components may not blend perfectly with the Vaadin design system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Want to create your own? Use this &lt;a href="https://github.com/vaadin/npm-addon-template" rel="noopener noreferrer"&gt;add-on project template&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the right model
&lt;/h2&gt;

&lt;p&gt;Which one should I use? Each type of Vaadin component has its place. The idea is not to pick just one and stick with it—it’s to use the best tool for the job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-only components for secure, logic-heavy forms and flows&lt;/li&gt;
&lt;li&gt;Client-server components for interactive, UX-rich widgets&lt;/li&gt;
&lt;li&gt;NPM-wrapped components for integrating modern web UIs quickly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The beauty of Vaadin is that these all coexist in the same app. You don’t need to compromise. You can treat your UI as a tree of components, each doing what it does best—on the server or the client. Your application integrates them on logical level: button is still fundamentally a button regardless of it's implementation. &lt;/p&gt;

&lt;p&gt;The real magic behind Vaadin components isn't about whether they look nice or use Java or TypeScript. It’s the &lt;strong&gt;flexibility&lt;/strong&gt; and &lt;strong&gt;maintainability&lt;/strong&gt;. You can build UIs in Java, TypeScript, or both. You choose where the logic lives. You validate securely and integrate freely.&lt;/p&gt;

&lt;p&gt;Most importantly, you don’t have to rewrite your app just to take advantage of something new. The component model is built for real-world apps that evolve over time.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Have you explored the different component types in your app? What kind of hybrid approach have you found most useful?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>java</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Third-Party Cookies Are Gone (Or Not). How Can I Still Embed Cross-Site Apps?</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Wed, 31 Jul 2024 14:32:00 +0000</pubDate>
      <link>https://forem.com/samiekblad/third-party-cookies-are-gone-how-can-i-still-embed-cross-site-apps-1nf1</link>
      <guid>https://forem.com/samiekblad/third-party-cookies-are-gone-how-can-i-still-embed-cross-site-apps-1nf1</guid>
      <description>&lt;p&gt;In 2024, there has been significant discussion about third-party cookies &lt;a href="https://developer.mozilla.org/en-US/blog/goodbye-third-party-cookies/" rel="noopener noreferrer"&gt;becoming obsolete&lt;/a&gt; or &lt;a href="https://privacysandbox.com/news/privacy-sandbox-update/" rel="noopener noreferrer"&gt;user configurable&lt;/a&gt;. While these changes are beneficial for user privacy, they complicate the lives of web application developers who want their applications or widgets to be shared across different websites. The main question remains: can I still make embedded web applications? How can we ensure that those still work?&lt;/p&gt;

&lt;p&gt;Here’s a brief guide on how to configure your web application to ensure it remains unaffected by upcoming changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-Side Configuration
&lt;/h2&gt;

&lt;p&gt;Assuming your web application is already designed for website embedding, you only need to configure a few things on the server side to keep your application up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Enable TLS for HTTPS
&lt;/h3&gt;

&lt;p&gt;While maintaining secure data transmission has been a best practice for a long time, enabling TLS (or SSL) for HTTPS is now mandatory. The method for enabling SSL depends on where your application is hosted. For instance, &lt;a href="https://fly.io/" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt; offers a &lt;code&gt;force_https = true&lt;/code&gt; option to configure apps to use HTTPS only.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configure Session Cookies
&lt;/h3&gt;

&lt;p&gt;Embedded applications often rely on session cookies to function correctly. Properly configuring these cookies with the necessary attributes is critical.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SameSite=None&lt;/strong&gt;: Means that the browser sends the cookie with both cross-site and same-site requests. The &lt;code&gt;Secure&lt;/code&gt; attribute must also be set when setting this value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt;: Ensures that the cookies are only sent over HTTPS, protecting them from being intercepted in transit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partitioned&lt;/strong&gt;: This attribute, part of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Privacy_sandbox/Partitioned_cookies" rel="noopener noreferrer"&gt;CHIPS&lt;/a&gt; (or Cookies Having Independent Partitioned State) proposal, ensures that cookies are partitioned based on the top-level site context, adding an extra layer of privacy by isolating cookies to the specific context. This is important since otherwise browsers will block the cookies in the future.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, your session cookie headers should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set-Cookie: PHPSESSID=abcde12345; path=/; HttpOnly; SameSite=Strict; Secure; Partitioned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set-Cookie: JSESSIONID=abcde12345; Path=/; HttpOnly; SameSite=Strict; Secure; Partitioned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Add Basic CORS Headers
&lt;/h3&gt;

&lt;p&gt;The W3C defines the &lt;a href="https://www.w3.org/TR/2020/SPSD-cors-20200602/" rel="noopener noreferrer"&gt;CORS protocol&lt;/a&gt;, which allows you to specify which sites can embed your content. Adding the appropriate CORS headers to your HTTP responses ensures that your content can be securely embedded across different origins. Some required during the preflight (OPTIONS) request and others during the actual (GET, POST,...) request.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sent During Preflight Requests:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access-Control-Allow-Methods&lt;/strong&gt;: Specifies the HTTP methods (e.g., GET, POST, PUT, DELETE) allowed when accessing the resource. This header is sent in response to a preflight request to inform the client of the methods supported by the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access-Control-Allow-Headers&lt;/strong&gt;: Lists the HTTP headers that can be used during the actual request. This is sent in the preflight response to let the client know which headers can be included in the actual request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access-Control-Max-Age&lt;/strong&gt;: Indicates how long the results of the preflight request can be cached by the client. This allows the client to cache the preflight response for a specified duration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sent During Actual Requests:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access-Control-Allow-Origin&lt;/strong&gt;: Specifies the origin(s) allowed to access the resource. You need to use an explicit list of allowed domains instead of the wildcard (&lt;code&gt;*&lt;/code&gt;). This header is sent in the actual response to indicate which origins have access to the resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access-Control-Allow-Credentials&lt;/strong&gt;: &lt;code&gt;true&lt;/code&gt; tells browsers that the server allows cross-origin HTTP requests to include credentials. This is sent either in preflight or in the actual response to indicate whether credentials (such as cookies or HTTP authentication) are allowed to be included in the real request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How you set these headers in HTTP responses depends on the framework and runtime environment you use, but the principles are the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Hosting Page
&lt;/h2&gt;

&lt;p&gt;Some HTTP headers need to be configured on the hosting page. Consider setting up the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy" rel="noopener noreferrer"&gt;Cross-Origin-Embedder-Policy&lt;/a&gt; and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy" rel="noopener noreferrer"&gt;Cross-Origin-Resource-Policy&lt;/a&gt;. By default, these policies allow embedding and resource loading, but configuring them helps enhance the security of your application by restricting unauthorized access and embedding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Setup: Seeing is Believing
&lt;/h2&gt;

&lt;p&gt;As you might know, my default full stack consists of &lt;a href="https://spring.io/projects/spring-boot" rel="noopener noreferrer"&gt;Spring Boot&lt;/a&gt; and &lt;a href="https://vaadin.com/flow" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt;, and that is what I use here too. You'll find my small demo live at: &lt;a href="https://samie.github.io/vaadin-cors-sample/" rel="noopener noreferrer"&gt;samie.github.io/vaadin-cors-sample&lt;/a&gt;. When using Chrome, test it with the &lt;a href="https://developers.google.com/privacy-sandbox/3pcd/prepare/test-for-breakage" rel="noopener noreferrer"&gt;third-party cookies disabled&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y7hsggq5klssbfuey67.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y7hsggq5klssbfuey67.png" alt="Image description" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Yes, embedding cross-site applications will remain a valid approach in the future. By carefully configuring your server to handle cookies and headers correctly, you can maintain secure and functional cross-origin embedding even as third-party cookies evolve. This ensures your applications or widgets can continue to work across different websites without compromising on security or functionality.&lt;/p&gt;

&lt;p&gt;Like always, &lt;a href="https://github.com/samie/vaadin-cors-sample" rel="noopener noreferrer"&gt;source code at GitHub&lt;/a&gt;. See you there! &lt;/p&gt;

</description>
      <category>cors</category>
      <category>cookies</category>
      <category>webapps</category>
    </item>
    <item>
      <title>UX vs. KPIs? Lessons from Copenhagen's Bike Rentals.</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Wed, 03 Jul 2024 10:05:00 +0000</pubDate>
      <link>https://forem.com/samiekblad/ux-vs-kpis-lessons-from-copenhagens-bike-rentals-1cgi</link>
      <guid>https://forem.com/samiekblad/ux-vs-kpis-lessons-from-copenhagens-bike-rentals-1cgi</guid>
      <description>&lt;p&gt;Story time. Recently we were visiting &lt;a href="https://en.wikipedia.org/wiki/Copenhagen" rel="noopener noreferrer"&gt;Copenhagen&lt;/a&gt;, the city known for the Little Mermaid and its vibrant biking culture. Eager to explore more of the city, we decided to rent bikes. The streets were lined with sleek, inviting rental bikes, and I picked the best-looking electric one. A QR code on the bike invited me to “Start here!”&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple as 1-2-3
&lt;/h2&gt;

&lt;p&gt;And that’s when the trouble began. The QR code led me to the company’s website, which advertised rental services to cities and communities. Not exactly user-friendly for someone just wanting a bike ride.&lt;/p&gt;

&lt;p&gt;Well, I headed to the App Store to find the company’s app. Two apps appeared. Neither clearly indicated which one was for me. After some digging, I figured out which one to install. The other app was for cities to manage their bike fleets.&lt;/p&gt;

&lt;p&gt;Upon launching the app, I was hit with a standard registration form: name, email, phone. This is where I hit a complete showstopper. The continue button was hidden underneath the virtual keyboard. Knowing many tricks iPhone didn’t help. Focusing on any of the fields popped up the keyboard and covered the continue button. We went for another bike rental.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about KPIs?
&lt;/h2&gt;

&lt;p&gt;I noticed plenty of unrented bikes from this company. In contrast, other bike rentals had almost all their bikes in use. I could imagine the frustration of their leadership team — high season, bikes sitting idle, new users almost nonexistent.&lt;/p&gt;

&lt;p&gt;Maybe Android users just are more likely to rent a bike? Maybe the competitors have better bikes? Do we have the bikes in the right spots?&lt;/p&gt;

&lt;p&gt;However, the lesson to learn here is clear: This isn’t a demographic issue or a need for expensive competitor research/analysis. It just a obvious UX problem. If the first touchpoint with your service is frustrating, users will abandon you quickly — they might not have a choice.&lt;/p&gt;

&lt;p&gt;Prioritizing real-life field testing is the only way to spot and validate issues like this. Make the user experience your top priority, and the other KPIs will follow.&lt;/p&gt;

&lt;p&gt;The saddest thing was that the next night they reorganized 10+ bikes, moved them to a better spot. And they all were there, unrented.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PS. I don’t want to mention any names or brands here, but if you happen to recognize yourself, I’m happy to provide further details and help. The bikes look awesome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ux</category>
      <category>kpi</category>
      <category>pm</category>
    </item>
    <item>
      <title>Happy Path: Web UI for Ollama using Java Only</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Tue, 21 May 2024 18:04:00 +0000</pubDate>
      <link>https://forem.com/samiekblad/happy-path-web-ui-for-ollama-using-java-only-6g1</link>
      <guid>https://forem.com/samiekblad/happy-path-web-ui-for-ollama-using-java-only-6g1</guid>
      <description>&lt;p&gt;I found that getting a local LLM up and running is becoming easier and easier, so it's time to update the guides. In this article, I'm setting up Ollama and building a minimal web UI—all in Java.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A full-stack application with a &lt;a href="https://vaadin.com/flow" rel="noopener noreferrer"&gt;Vaadin frontend&lt;/a&gt; and Spring Boot backend.&lt;/li&gt;
&lt;li&gt;Real-time chatting capability using a local &lt;a href="https://hub.docker.com/r/ollama/ollama" rel="noopener noreferrer"&gt;Ollama in a container&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Dynamic message streaming and display of Markdown for the UX you would expect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will need the following installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java 21&lt;/li&gt;
&lt;li&gt;Maven for building the app&lt;/li&gt;
&lt;li&gt;Local Docker container environment for running Ollama&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install and Run Ollama Container
&lt;/h2&gt;

&lt;p&gt;First, set up the Ollama Docker container:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we need to install some of the supported LLM models, with 'mistral' being the default. This will take a while to download: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker exec ollama ollama pull mistral


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once done, you can check if the model is up and running by calling the REST API from the command line. For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

curl http://localhost:11434/api/chat -d '{"model": "mistral", "messages": [{"role": "user", "content": "is black darker than white?"}], "stream":false}'


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Creating the Web UI
&lt;/h2&gt;

&lt;p&gt;Generate a new Spring Boot project using Spring Initializr. You can configure the dependencies you need, but for this, we only need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ollama - Spring AI APIs for the local LLM&lt;/li&gt;
&lt;li&gt;Vaadin - for Java web UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://start.spring.io/#!type=maven-project&amp;amp;language=java&amp;amp;platformVersion=3.2.5&amp;amp;packaging=jar&amp;amp;jvmVersion=17&amp;amp;groupId=com.example&amp;amp;artifactId=demo&amp;amp;name=demo&amp;amp;description=Demo%20project%20for%20Spring%20Boot&amp;amp;packageName=com.example.demo&amp;amp;dependencies=vaadin,spring-ai-ollama,devtools" rel="noopener noreferrer"&gt;Here is direct link to the configuration&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This will create a ready-to-run project that you can import into your Java IDE.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding Extras
&lt;/h2&gt;

&lt;p&gt;To use Vaadin add-ons, configure the dependencies in your &lt;code&gt;pom.xml&lt;/code&gt;. We want to use the &lt;a href="https://vaadin.com/directory/component/flow-viritin" rel="noopener noreferrer"&gt;Viritin add-on&lt;/a&gt;  for handy Markdown streaming.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;in.virit&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;viritin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.8.14&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Building the Web UI
&lt;/h2&gt;

&lt;p&gt;We still need the UI. Create a MainView class to handle the chat UI and interaction with the local LLM. Injecting Spring beans into the Vaadin UI, this is all that is needed:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// map view to the root&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;VerticalLayout&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;VerticalLayout&lt;/span&gt; &lt;span class="n"&gt;messageList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VerticalLayout&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;Scroller&lt;/span&gt; &lt;span class="n"&gt;messageScroller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Scroller&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageList&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;MessageInput&lt;/span&gt; &lt;span class="n"&gt;messageInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MessageInput&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;MainView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StreamingChatClient&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageScroller&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messageInput&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;setSizeFull&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;setMargin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;messageScroller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSizeFull&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;messageInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setWidthFull&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Add system message to help the AI to behave&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SystemMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Only if the user asks you about Vaadin, reply in bro style. Always show a piece a code."&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;messageInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addSubmitListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ev&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Add use input as markdown message&lt;/span&gt;
            &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
            &lt;span class="n"&gt;messageList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MarkdownMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="s"&gt;"Me"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

            &lt;span class="c1"&gt;// Placeholder message for the upcoming AI reply&lt;/span&gt;
            &lt;span class="nc"&gt;MarkdownMessage&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MarkdownMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Assistant"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;messageList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Ask AI and stream back the reply to UI&lt;/span&gt;
            &lt;span class="nc"&gt;Prompt&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Prompt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doOnComplete&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AssistantMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMarkdown&lt;/span&gt;&lt;span class="o"&gt;())))&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cr&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;appendMarkdownAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResult&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getOutput&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getContent&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
            &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scrollIntoView&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can run the application either by running the main &lt;code&gt;Application.java&lt;/code&gt; class from your IDE or from the command line using Maven: `mvn spring-boot:run'. &lt;/p&gt;

&lt;p&gt;Now we can chat with the Ollama Mistral model locally at &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fri56be5ts708a2u2ubj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fri56be5ts708a2u2ubj8.png" alt="Ollama Web Chat UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was another one of those step-by-step guides that I wrote mostly for myself when setting up a small demo application. The full &lt;a href="https://github.com/samie/spring-ai-chat/tree/ollama" rel="noopener noreferrer"&gt;app is in GitHub&lt;/a&gt; (note the branch), if you wish to use it as a starting point for your own demos.&lt;/p&gt;

&lt;p&gt;If you find it useful, let me know in the comments. Thanks!&lt;/p&gt;

</description>
      <category>java</category>
      <category>ollama</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Running Java App using Google Cloud Run</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Wed, 21 Feb 2024 21:05:00 +0000</pubDate>
      <link>https://forem.com/samiekblad/running-java-app-using-google-cloud-run-509d</link>
      <guid>https://forem.com/samiekblad/running-java-app-using-google-cloud-run-509d</guid>
      <description>&lt;p&gt;In our journey we've created a Net Promoter Score (NPS) widget and application that saves data to a Google Spreadsheet. This time, we take the app to the cloud.&lt;/p&gt;

&lt;p&gt;Since we already used Google Sheets for storage, it felt natural to use Google Cloud Run for deployment. It offers a fully managed platform with enough of resources that allows applications to run in containers accessible via the web. Additionally, when using the Cloud Console, we do not need to install any local tools, streamlining the deployment process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clone, Build, Deploy
&lt;/h2&gt;

&lt;p&gt;Deploying Java applications in the cloud has been a challenge. However, recent advancements have greatly simplified this process. Utilizing build packs or a Dockerfile, we can build and deploy Maven applications quickly. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Navigate to &lt;a href="https://shell.cloud.google.com/" rel="noopener noreferrer"&gt;shell.cloud.google.com&lt;/a&gt; to access Google Cloud's integrated shell environment. This is a full-featured IDE in your browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Clone the NPS project from GitHub &lt;a href="https://github.com/samie/nps" rel="noopener noreferrer"&gt;github.com/samie/nps&lt;/a&gt;. This repository contains a Vaadin add-on itself, but also the demo application project we want to deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; If your project lacks a Dockerfile, you need to add one. You can use Java build packs, but a &lt;a href="https://github.com/samie/nps/blob/v24/demo/Dockerfile" rel="noopener noreferrer"&gt;two-phase Dockerfile&lt;/a&gt; gives you a more definitive build. This first downloads all dependencies before creating your Vaadin application's production build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2ssxevl6h8dvfze511t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2ssxevl6h8dvfze511t.png" alt="Cloud Run automatically finds the Docerfiles in the project" width="606" height="526"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Build and deploy to Cloud Run. Select the correct Dockerfile, in this case, demo/Dockerfile, and initiate the build. Deployment build may take several minutes (typically 3 to 4 minutes).&lt;/p&gt;

&lt;p&gt;Upon successful deployment, you will encounter an error related to Google Sheets access:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzpwitcr9gopy58nju4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzpwitcr9gopy58nju4k.png" alt="java.lang.RuntimeException due to missing credentials." width="706" height="200"&gt;&lt;/a&gt;{ width=60% }&lt;/p&gt;

&lt;p&gt;This was expected. We need the credentials to access the Google Sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Secrets
&lt;/h2&gt;

&lt;p&gt;Managing environment variables and secret files in cloud environments can be tricky. Google provides a "Secret Manager" tool where you can safely manage your application-specific secrets. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u2l20exp5prnqydclbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u2l20exp5prnqydclbw.png" alt="Create new secret for service account credentials" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Create a Secret&lt;br&gt;
Create a new secret containing the service account credentials to access the Google Sheet at &lt;a href="https://console.cloud.google.com/security/secret-manager" rel="noopener noreferrer"&gt;console.cloud.google.com/security/secret-manager&lt;/a&gt;. If you already created a key for the Service Account, you can paste the content of that file, or you can create a new key at &lt;a href="https://console.cloud.google.com/iam-admin/serviceaccounts" rel="noopener noreferrer"&gt;console.cloud.google.com/iam-admin/serviceaccounts&lt;/a&gt; and choose tab "Keys" in the correct service account. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Make Secrets Available&lt;br&gt;
Make these secrets available to your deployed containers by creating a special "secrets" volume under the "Volumes" tab. Then, mount this volume to /local on the container through the "Containers" tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Use secrets in the code&lt;br&gt;
Adjust your application code (in this case, in &lt;a href="https://github.com/samie/nps/blob/v24/demo/src/main/java/com/example/application/views/feedback/NPSView.java#L80" rel="noopener noreferrer"&gt;NPSView.java&lt;/a&gt;) to check for the proper credentials file location at &lt;code&gt;/local/&amp;lt;secret_name&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;With the secrets configured and your application adjusted, click "Deploy" to finalize the process. Your application should now be operational!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz8t9mgznyqg96u1tw7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz8t9mgznyqg96u1tw7l.png" alt="Application successfully running" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The NPS feedback view is running at &lt;a href="https://nps-u342s5ewoq-uc.a.run.app/" rel="noopener noreferrer"&gt;nps-u342s5ewoq-uc.a.run.app/&lt;/a&gt;&lt;br&gt;
And the embedded page sample at &lt;a href="https://nps-u342s5ewoq-uc.a.run.app/example.html" rel="noopener noreferrer"&gt;nps-u342s5ewoq-uc.a.run.app/example.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I was captivated by the simplicity of cloud-only tooling. Building directly from GitHub in browser IDE without local installations was refreshingly straightforward. The most challenging part was ensuring secure access to sensitive files, and I was a bit disappointed with how little synergy GCP offered for my architecture. Nevertheless, once you navigate its terminology and enable all the needed services, Google Cloud Platform provides robust tools to build anything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuplwnt3ppmzw92ixjhyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuplwnt3ppmzw92ixjhyh.png" alt="Java application up and running in cloud with session affinity" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This marks the end of the series. It was initially a set of personal memos, from crafting a custom Vaadin widget, calling APIs, and integrating cloud services. But it grew into this guide, the path of deploying Java-only applications in the cloud. I hope you enjoyed the journey as much as I did.I thank you for joining this journey. &lt;/p&gt;

&lt;p&gt;Happy coding, and let me know what you built! &lt;/p&gt;

</description>
      <category>java</category>
      <category>vaadin</category>
      <category>googlecloudrun</category>
    </item>
    <item>
      <title>Integrating a Vaadin Web Application into External Webpages</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Fri, 13 Oct 2023 10:39:41 +0000</pubDate>
      <link>https://forem.com/samiekblad/integrating-a-vaadin-web-application-into-external-webpages-370a</link>
      <guid>https://forem.com/samiekblad/integrating-a-vaadin-web-application-into-external-webpages-370a</guid>
      <description>&lt;p&gt;Have you created a web application that would make more sense embedded into another web page? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibhcgttgbpvac8mif0ug.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibhcgttgbpvac8mif0ug.gif" alt="Embedded custom NPS Survey Written in Java"&gt;&lt;/a&gt;&lt;br&gt;
In my &lt;a href="https://dev.to/samiekblad/push-data-to-google-sheets-from-your-java-app-4hio"&gt;previous blog&lt;/a&gt;, I created an NPS (Net Promoter Score) widget that stores data directly into Google Sheets, and in this blog, we look at what it takes to embed this single Vaadin-based view as a part of any other web page. After all, you want to collect user feedback like NPS in the right place and context.&lt;/p&gt;

&lt;p&gt;Our primariry goal is to have the web application embedded into another web page. Furthermore, having it in a popup instead of directly on the page makes sense. The &lt;a href="https://github.com/samie/nps/blob/v24/demo/src/main/resources/static/example.html" rel="noopener noreferrer"&gt;example.html&lt;/a&gt; is our host page with two examples: direct embedding and a JavaScript popup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export a web component...
&lt;/h2&gt;

&lt;p&gt;First, we need to export the application to use from another page. In Vaadin, a utility called &lt;a href="https://vaadin.com/docs/latest/integrations/embedding/exporter" rel="noopener noreferrer"&gt;WebComponentExporter&lt;/a&gt; takes any Vaadin view and exports it as a single custom element. To export a custom element called &lt;code&gt;nps-feedback&lt;/code&gt; we create the following class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public class CustomElementExporter extends WebComponentExporter&amp;lt;NPSView&amp;gt; {
    public CustomElementExporter() {        
        super("nps-feedback");
        // ... [further configuration goes here] ...
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can add attribute handling to allow NPS view configuration when using it as custom element. Like passing the header title: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

addProperty("header", "Thank you for visiting us").onChange(NPSView::setHeader);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This adds a element property named &lt;code&gt;header&lt;/code&gt; with a default value . Whenever the property changes, the &lt;code&gt;setHeader&lt;/code&gt; method of &lt;code&gt;NPSView&lt;/code&gt; will be called with the new value.&lt;/p&gt;

&lt;h2&gt;
  
  
  ...and embed to a page.
&lt;/h2&gt;

&lt;p&gt;Let's jump into our &lt;a href="https://github.com/samie/nps/blob/v24/demo/src/main/resources/static/example.html" rel="noopener noreferrer"&gt;example host page&lt;/a&gt; and see how to embed the NPS feedback there. &lt;/p&gt;

&lt;p&gt;The simplest way is to include the component just as-is: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;script type="module" src="./web-component/nps-feedback.js"&amp;lt;/script&amp;gt;
&amp;lt;nps-feedback id="nps-web-component" product="page" header="Thanks for being here" question="How like you are to come back?"&amp;gt;&amp;lt;/nps-feedback&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will import show the web component to the page. However, I wanted to have the NPS survey presented in a popup dynamically. So, let's check further. &lt;/p&gt;

&lt;h2&gt;
  
  
  Micro frontend in a dynamic popup
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://micro-frontends.org/" rel="noopener noreferrer"&gt;Micro frontends&lt;/a&gt; is an architectural style that extends the concepts of microservices to the frontend. It advocates splitting the frontend into smaller, more manageable pieces, each deployable independently. &lt;/p&gt;

&lt;p&gt;For our use case of NPS feedback, including it non-intrusively as a popup that the user can open (or can be opened programmatically) on desired pages often makes much sense. &lt;/p&gt;

&lt;p&gt;The popup functionality is in a separate JS file. Here, the &lt;a href="https://github.com/samie/nps/blob/v24/demo/src/main/resources/static/nps.js" rel="noopener noreferrer"&gt;npm.js module&lt;/a&gt; also imports the web component dynamically when needed. It exports the popup functionality in logical methods that we import in our host page: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;script type="module"&amp;gt;
import {createNpsPopup, openNpsPopup, closeNpsPopup} from './nps.js';
createNpsPopup("myappfeedback", "Thank you for visiting the service");
&amp;lt;/script&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To create a button on the host page to open the created popup, you can attach a click listener as follows. &lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;document.querySelector("#openNpsBtn").addEventListener('click',openNpsPopup);&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Embedding your NPS widget as a micro frontend within another webpage is a leap towards enhanced user engagement and modular code. Definitely the way to go with small apps like this. &lt;/p&gt;

&lt;p&gt;If you wish to explore more, let the &lt;a href="https://github.com/samie/nps" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; be your launchpad into an ecosystem where web applications no longer need to be monoliths. &lt;/p&gt;

</description>
      <category>java</category>
      <category>vaadin</category>
      <category>microfrontends</category>
    </item>
    <item>
      <title>Push data to Google Sheets from your Java app</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Thu, 29 Jun 2023 14:46:01 +0000</pubDate>
      <link>https://forem.com/samiekblad/push-data-to-google-sheets-from-your-java-app-4hio</link>
      <guid>https://forem.com/samiekblad/push-data-to-google-sheets-from-your-java-app-4hio</guid>
      <description>&lt;p&gt;The &lt;a href="https://dev.to/samiekblad/creating-custom-component-for-nps-feedback-4p4e"&gt;previous article&lt;/a&gt; explained how to make a reusable &lt;a href="https://vaadin.com/directory/component/nps" rel="noopener noreferrer"&gt;NPS feedback component&lt;/a&gt;. After writing the article, I made a few tweaks to the component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A square style for the buttons&lt;/li&gt;
&lt;li&gt;Customizable texts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are good additions to make the component usable in any application but to get further, we need something outside the UI: Store the NPS feedback data somewhere in the cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp29oppx2l701jgz7f3v2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp29oppx2l701jgz7f3v2.png" alt="Image description" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the spreadsheet for NPS feedback
&lt;/h2&gt;

&lt;p&gt;Isn't that obvious when you say that aloud? After all, people who collect this type of user feedback are also familiar with spreadsheets. Let's keep it simple for them. &lt;/p&gt;

&lt;p&gt;For the scope of this NPS application, I used Google Sheets, which is more configuration than programming. The spreadsheet layout is simple enough: timestamp, anonymous user id, and the NPS score columns. I also added a "Feedback" column for future improvements. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Google Service Account
&lt;/h2&gt;

&lt;p&gt;This is the configuration part you must do in Google Cloud. Service Accounts are the application-specific counterpart of user accounts. To create one to access Google Sheets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select or create a new project, and add a new &lt;a href="https://console.cloud.google.com/iam-admin/serviceaccounts" rel="noopener noreferrer"&gt;Service Account&lt;/a&gt; without other roles or permissions. &lt;/li&gt;
&lt;li&gt;Go to "Manage Keys", click "Add key", and create and download a new Service Account authentication JSON key. Store it somewhere safe outside the code repository. &lt;/li&gt;
&lt;li&gt;Enable the &lt;a href="https://console.cloud.google.com/apis/library/sheets.googleapis.com" rel="noopener noreferrer"&gt;Google Sheets API&lt;/a&gt; for the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzocqswha1fcqj9nhw0r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzocqswha1fcqj9nhw0r.png" alt="Image description" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorize the Service Account to access your sheet
&lt;/h2&gt;

&lt;p&gt;Now that you have your Service Account set up, we can give it access to the document you created:&lt;br&gt;
Copy the email of your Service Account. It is the one like '&lt;a href="mailto:app-name@project-name-123456.iam.gserviceaccount.com"&gt;app-name@project-name-123456.iam.gserviceaccount.com&lt;/a&gt;'&lt;br&gt;
Grant editor permission to this account in your NPS Spreadsheet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyixl6s820wr19c5l3yvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyixl6s820wr19c5l3yvj.png" alt="Image description" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Libraries and code
&lt;/h2&gt;

&lt;p&gt;We are back in IDE. One service account and the spreadsheet has been created. It's time to access them from your application code.&lt;/p&gt;

&lt;p&gt;When building new functionality, I like to start by "writing the code I like". This helps me design the API I need for the use case. In this case, I started with the following in my &lt;code&gt;ValueChangeListener&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String userId = "" + UI.getCurrent().hashCode(); // for testing only
int npsScore =  e.getValue(); // User fee
FeedbackSheet sheet = new FeedbackSheet();
sheet.append(userId, npsScore);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, most of the implementation would go to the &lt;a href="https://github.com/samie/nps/blob/v24/demo/src/main/java/com/example/application/data/FeedbackSheet.java" rel="noopener noreferrer"&gt;FeedbackSheet.java&lt;/a&gt;. Logically it does the following things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the &lt;code&gt;ServiceAccountCredentials&lt;/code&gt; from a JSON file&lt;/li&gt;
&lt;li&gt;Creates a &lt;code&gt;Sheets&lt;/code&gt; instance to run authorized actions for a specified sheet.
An &lt;code&gt;append&lt;/code&gt; method adds a new row to the first sheet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the code part. To make this work, you need to add a couple of dependencies to &lt;code&gt;pom.xml&lt;/code&gt;. They are the Google Java libraries for Google Sheets, OAauth2 and JSON parsing:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update 2024-10-24: latest versions of the libraries.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.google.api-client&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;google-api-client&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.7.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.google.apis&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;google-api-services-sheets&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;v4-rev20241008-2.0.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.google.oauth-client&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;google-oauth-client&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.36.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.google.http-client&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;google-http-client-gson&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.45.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Findings and Conclusion
&lt;/h2&gt;

&lt;p&gt;Using Google Sheets to store data is a manageable amount of Java code - once you find it. There are a few good articles like &lt;a href="https://www.baeldung.com/google-sheets-java-client" rel="noopener noreferrer"&gt;this one from Baeldung&lt;/a&gt;, but getting the code to match the configuration takes some work. Also, Google Cloud is a living thing; many other articles I found outdated in 2023. &lt;/p&gt;

&lt;p&gt;The way I use it here has the benefit of the spreadsheet owner granting and revoking access to the documents to an app, but it does not offer any fancy impersonation possibilities which would need a &lt;a href="https://developers.google.com/sheets/api/quickstart/java#configure_the_oauth_consent_screen" rel="noopener noreferrer"&gt;OAUth consent dialog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to try locally, you'll find the &lt;a href="https://github.com/samie/nps/tree/v24/demo" rel="noopener noreferrer"&gt;sample code&lt;/a&gt; in GitHub and &lt;a href="https://docs.google.com/spreadsheets/d/1aTfU2_XuZU-HgUhSBu4_oB_gB4hhro-RzNsdN9_8YX8/edit?usp=sharing" rel="noopener noreferrer"&gt;the sample feedback spreadsheet&lt;/a&gt; in Google Sheets. &lt;/p&gt;

</description>
      <category>nps</category>
      <category>googlesheets</category>
      <category>vaadin</category>
      <category>java</category>
    </item>
    <item>
      <title>Creating Custom Component for NPS Feedback</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Sun, 30 Apr 2023 12:38:39 +0000</pubDate>
      <link>https://forem.com/samiekblad/creating-custom-component-for-nps-feedback-4p4e</link>
      <guid>https://forem.com/samiekblad/creating-custom-component-for-nps-feedback-4p4e</guid>
      <description>&lt;p&gt;&lt;em&gt;(Update 2023-05-24: The component is available as add-on at &lt;a href="https://vaadin.com/directory/component/nps" rel="noopener noreferrer"&gt;vaadin.com/directory/component/nps&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We had a project that needed to implement the famous &lt;a href="https://en.wikipedia.org/wiki/Net_promoter_score" rel="noopener noreferrer"&gt;Net Promoter Score&lt;/a&gt; or NPS feedback. Quite boring, TBH. Just one of those relatively trivial UIs that you need to have. (&lt;a href="https://konmari.com/marie-kondo-rules-of-tidying-sparks-joy/" rel="noopener noreferrer"&gt;"Does this spark joy?"&lt;/a&gt; It would most definitely be a clear &lt;em&gt;"No."&lt;/em&gt; ). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3e7pmbotd19rma8vc72x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3e7pmbotd19rma8vc72x.png" alt="Image description" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, there is a good reason for collecting user feedback like this, and we wanted to have it. I immediately thought that something like this would make sense as a separate component (mainly to &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;avoid doing it again&lt;/a&gt;), so I decided to do that. &lt;/p&gt;

&lt;p&gt;Here is the process I went through to build a reusable version. This blog is again for &lt;a href="https://vaadin.com/directory/" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt;, but the principles and the &lt;em&gt;"build reusable components"&lt;/em&gt; -thinking makes sense on any web or IU framework. &lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new add-on project
&lt;/h2&gt;

&lt;p&gt;The first step in reusability: We want to create a separate add-on project. I used the Vaadin default &lt;a href="https://github.com/vaadin/addon-template" rel="noopener noreferrer"&gt;addon-template&lt;/a&gt; from GitHub and created a new repository under my account. This project is a good starting point as it provides the boilerplate for packaging and distribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clone the project locally
&lt;/h2&gt;

&lt;p&gt;Next, clone the repository project locally in Visual Studio Code. I'm using the &lt;a href="https://code.visualstudio.com/docs/devcontainers/containers" rel="noopener noreferrer"&gt;Dev Containers&lt;/a&gt; to run the project separately from others. There is a ready-made Java container to which I add Maven as a build system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vaadin Tip: Upgrade to V24&lt;br&gt;
At the time of writing, the template project was still on older Vaadin 23. So a minor update is needed to pom.xml to make it compatible with the latest Vaadin version.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vaadin version to 24.0.5&lt;/li&gt;
&lt;li&gt;Java version to 17&lt;/li&gt;
&lt;li&gt;Jetty version to 11.0.15&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The UI Code
&lt;/h2&gt;

&lt;p&gt;Like I said in the beginning, the UI is pretty simple, and the code to create that it's not too much:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public NPS() {
    Span title = new Span("On a scale of 1 to 10, how likely would you recommend this to your friend or colleague?");
    HorizontalLayout buttons = new HorizontalLayout();
    buttons.add(new Text("Not likely"));
    for (int i = 1; i &amp;lt;= 10; i++) {
        Button button = new Button(String.valueOf(i), e -&amp;gt; {
            int score = Integer.parseInt(e.getSource().getText());
            Notification.show("Changed to "+score);
        });  
        buttons.add(button);
    }
    buttons.add(new Text("Very likely"));
    this.add(title, buttons);    
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the title and list of buttons in a horizontal layout. This is already usable as-is in your Vaadin application if you like to try it and use &lt;a href="https://levelup.gitconnected.com/copy-pasting-code-can-be-devastating-ae1c8252c8fa" rel="noopener noreferrer"&gt;"copy-paste reusability"&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Thinking about component reusability
&lt;/h2&gt;

&lt;p&gt;When making reusable components, you want to consider a few things. The simplest way to document your component is to create a self-explanatory API. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Component UX.&lt;/em&gt; Do you need keyboard navigation? Mouse hover? Shortcuts? I suggest staying safe, combining &lt;a href="https://vaadin.com/docs/latest/components" rel="noopener noreferrer"&gt;existing components&lt;/a&gt;, and making your custom solutions sparingly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component configuration&lt;/strong&gt; or DX. You want to be able to change the text and behaviour in various ways. The &lt;a href="https://twitter.com/samiekblad/status/1598265147998470152?s=20" rel="noopener noreferrer"&gt;combinations can get tricky&lt;/a&gt;, so you want to have various scenarios well-tested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI integration.&lt;/strong&gt; Is your component to be used alone (i.e., single-component app)? Will it be in a pop-up, or will it be part of that other UI? 
Data integration. Are your component emitting events? Does it support data binding? How much data to cache on the server, and how much can the browser handle?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility.&lt;/strong&gt; What framework version are you using? What version do you expect your user to be using? How about the Java version? Remember, dependencies work much like &lt;a href="https://en.wikipedia.org/wiki/Venn_diagram" rel="noopener noreferrer"&gt;Venn diagrams&lt;/a&gt;, so keep the list short.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing.&lt;/strong&gt; How to automate tests. As said, the configuration of states might get tricky, so you want to be good here to make sure to find the edge cases. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation.&lt;/strong&gt; This part should be straightforward if you did good job with the API. Focus on describing the developer use cases and skip the "this method does X" style docs altogether. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It might take a round or two to get everything right, but best to start with a "real app" where you use it and write code that uses your component. Not aiming for full-blown &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development" rel="noopener noreferrer"&gt;TDD&lt;/a&gt;, this is what I made for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final NPS nps = new NPS();
nps.setId("nps");  // This is for automated tests

// Button to start the feed process
add(new Button("Ask NPS", e -&amp;gt; {
    add(nps);
}));

// Get the score and remove component
nps.addValueChangeListener(e-&amp;gt; {
    Notification.show("Value changed from "+e.getOldValue()+" to "+e.getValue());
    add(new Paragraph("NPS: " +e.getOldValue()+" -&amp;gt; "+e.getValue()));
    remove(nps);
});

// Edit the the score
add(new Button("Edit score", e -&amp;gt; {
    nps.setValue((int)Math.ceil(Math.random()*10));
    add(nps);
}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is implemented in &lt;code&gt;AddonView.java&lt;/code&gt;, also used for automated tests. It is not the real thing, but simulating scenarios we had in the actual application.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic business logic
&lt;/h2&gt;

&lt;p&gt;Since the NPS component is clearly a "field component" with a single editable value, we can reuse the framework functionality here. Extending &lt;a href="https://vaadin.com/docs/latest/components/custom-field" rel="noopener noreferrer"&gt;CustomField&lt;/a&gt; gives us most of the functions we need for value input and events. We only need to implement the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected Integer generateModelValue() {
    return this.score;
}

protected void setPresentationValue(Integer score) {
    this.score = score;        
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test case already revealed a problem with the read-only support. We want the value to be visible, yet the input is disabled. The requires two more methods to be implemented. &lt;/p&gt;

&lt;p&gt;First, highlighting the selection. We use the "success color" to do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void updateButtonSate() {
    this.buttons
        .getChildren()
        .filter(c -&amp;gt; c instanceof Button)
        .forEach(c -&amp;gt; {
            Button b = (Button)c;
            if (score != null &amp;amp;&amp;amp; score.equals(Integer.parseInt(b.getText()))) {
                b.addClassName(LumoUtility.Background.SUCCESS_50);
                b.addClassName(LumoUtility.TextColor.SUCCESS_CONTRAST);
            } else {
                b.removeClassName(LumoUtility.Background.SUCCESS_50);
                b.removeClassName(LumoUtility.TextColor.SUCCESS_CONTRAST);
            };
        });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then another one to disable the buttons when we are in read-only state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void setReadOnly(boolean readOnly) {
    super.setReadOnly(readOnly);
    this.buttons
        .getChildren()
        .filter(c -&amp;gt; c instanceof Button)
        .forEach(c -&amp;gt; ((Button)c).setEnabled(!readOnly));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the events, we got the component value bookkeeping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release, release
&lt;/h2&gt;

&lt;p&gt;"&lt;a href="https://en.wikipedia.org/wiki/Release_early,_release_often" rel="noopener noreferrer"&gt;Release early, release often&lt;/a&gt;", correct? This is all you need to create a simple application that collects NPS feedback from your users. As you see, it is only the web UI part, and you still need to store data and calculate the final score. &lt;/p&gt;

&lt;p&gt;You may have your custom database, and we still need to package the component for distribution, but I'll show some simple options in the next blog post. In the meantime: checkout the component at &lt;a href="https://github.com/samie/nps" rel="noopener noreferrer"&gt;github.com/samie/nps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This reusability actually sparks joy, but now it is your turn: &lt;em&gt;On a scale of 1 to 10, how likely would you recommend this NPS survey to your friend or colleague?&lt;/em&gt; Leave a comment below.&lt;/p&gt;

</description>
      <category>nps</category>
      <category>component</category>
      <category>vaadin</category>
      <category>java</category>
    </item>
    <item>
      <title>Happy Path: Adding speech recognition to Vaadin apps.</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Tue, 18 Apr 2023 14:15:32 +0000</pubDate>
      <link>https://forem.com/samiekblad/happy-path-adding-speech-recognition-to-vaadin-apps-3abm</link>
      <guid>https://forem.com/samiekblad/happy-path-adding-speech-recognition-to-vaadin-apps-3abm</guid>
      <description>&lt;p&gt;The previous blog post added &lt;a href="https://dev.to/samiekblad/always-listening-voice-commands-for-vaadin-web-applications-3bhp"&gt;wake word detection&lt;/a&gt; for a Vaadin application using Picovoice. That is a robust in-browser approach to creating always-listening apps. You might want to use that for full speech recognition, but also the draft standard &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition" rel="noopener noreferrer"&gt;web speech API&lt;/a&gt; also provides a way to voice-enable applications. &lt;/p&gt;

&lt;p&gt;There are a few ways you can &lt;a href="https://dev.to/samiekblad/whats-the-deal-with-vaadin-add-ons-2f1n"&gt;integrate web APIs&lt;/a&gt;. This time we look at how to use web speech API for speech recognition in &lt;a href="https://vaadin.com/flow" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt; by integrating a ready-made add-on from Vaadin Directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose an add-on from Vaadin Directory
&lt;/h2&gt;

&lt;p&gt;Head to &lt;a href="https://vaadin.com/directory/?q=speech" rel="noopener noreferrer"&gt;vaadin.com/directory&lt;/a&gt; and search for &lt;em&gt;"speech"&lt;/em&gt;. Pick an add-on, such as "Voice Recognition", and click the install button on the top-right to see the different ways of installing: get the necessary dependencies, download, or in this case: create a new project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2w3k45holgwstzh2jdb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2w3k45holgwstzh2jdb3.png" alt="Image description" width="477" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Vaadin project with the add-on
&lt;/h2&gt;

&lt;p&gt;Click on "Create Project" and it starts downloading a Zip file with the full Maven project setup. Extract the zip and import it into your IDE. In IntelliJ, use &lt;code&gt;New&lt;/code&gt; --&amp;gt; &lt;code&gt;Project from Existing Sources...&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Implement Speech Recognition in Vaadin UI
&lt;/h2&gt;

&lt;p&gt;After opening the Vaadin project in your IDE, locate the main View class. I edited the "HelloWorldView.java" which has some sample code in it. Head back to &lt;a href="https://vaadin.com/directory/component/voice-recognition" rel="noopener noreferrer"&gt;the add-on page&lt;/a&gt; and copy and paste the sample code from the add-on's documentation into the constructor of the view class. Just replace the previous sample code. &lt;/p&gt;

&lt;p&gt;Run the Application class in debug and start the development server. Because we are adding a new add-on, this might take a while, but be patient. &lt;/p&gt;

&lt;p&gt;Click the start button in the UI and grant permission to use the microphone. Try to say something and see how well it performs. On the first try, the browser will ask your permission to access the mic. Remember, at the time being, the speech recognition API is only available in &lt;a href="https://caniuse.com/speech-recognition" rel="noopener noreferrer"&gt;Chrome and Safari&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Customize and add your own commands
&lt;/h2&gt;

&lt;p&gt;Now you can implement your custom application-specific functions. For example, modifying the code in the event listener to show a Vaadin standard &lt;a href="https://vaadin.com/docs/latest/components/notification" rel="noopener noreferrer"&gt;Notification&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"show notification"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;voiceRecognition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addResultListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSpeech&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indexOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                          &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
     &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo0mlk0atnel395jyff6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo0mlk0atnel395jyff6.png" alt="Image description" width="697" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That was a simple way to create a voice commands for Vaadin web application. While still limited by browser support, speech recognition opens up exciting possibilities for enhancing user experience in web applications. Share your thoughts and experiences in the comments below, and stay tuned for more.&lt;/p&gt;

</description>
      <category>java</category>
      <category>vaadin</category>
      <category>webdev</category>
      <category>speechrecognition</category>
    </item>
    <item>
      <title>Happy path: Building a Chatbot in Vaadin with OpenAI</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Tue, 11 Apr 2023 14:12:54 +0000</pubDate>
      <link>https://forem.com/samiekblad/happy-path-building-a-chatbot-in-vaadin-with-openai-4b8a</link>
      <guid>https://forem.com/samiekblad/happy-path-building-a-chatbot-in-vaadin-with-openai-4b8a</guid>
      <description>&lt;p&gt;Want to build better version of ChatGPT? Here is how to use OpenAI's &lt;a href="https://platform.openai.com/docs/guides/chat/chat-completions-beta" rel="noopener noreferrer"&gt;Chat completions API&lt;/a&gt; to add a robot participant to your &lt;a href="https://vaadin.com/flow" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt; chat app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyord4c6nho6uo9f7ha8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyord4c6nho6uo9f7ha8z.png" alt="Image description" width="800" height="822"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new chat app project
&lt;/h2&gt;

&lt;p&gt;Using the service at &lt;a href="https://start.vaadin.com/" rel="noopener noreferrer"&gt;start.vaadin.com&lt;/a&gt;, create a new Vaadin application. There actually is a complete chat example template, but the "hello world" sample is enough for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a simple chat UI
&lt;/h2&gt;

&lt;p&gt;Using &lt;a href="https://vaadin.com/docs/latest/components/message-list" rel="noopener noreferrer"&gt;MessageList&lt;/a&gt; and &lt;a href="https://vaadin.com/docs/latest/components/message-input" rel="noopener noreferrer"&gt;MessageInput&lt;/a&gt; from Vaadin, create a simple chat UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;VerticalLayout&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MessageList&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MessageInput&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ChatView&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MessageList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MessageInput&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addSubmitListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to make everything align nicely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHorizontalComponentAlignment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CENTER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPadding&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Leave some white space&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHeightFull&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// We maximize to window&lt;/span&gt;
&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSizeFull&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Chat takes most of the space&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setWidthFull&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Full width only&lt;/span&gt;
&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMaxWidth&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"800px"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Limit the width&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMaxWidth&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"800px"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create OpenAI Java API to call
&lt;/h2&gt;

&lt;p&gt;The next step is the interesting one. There is a Java API for OpenAI, but can ChatGPT create a simple REST API to call itself from Java? It took a few steps to get right, but yes, it can. Here is what I asked so you get the idea...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write a simple Java class called "OpenAI" to call completions to chat API with one "send" method that takes the latest user input as a parameter and returns updated chat messages. Provide inner classes "ChatRequest" and "ChatResponse" and use Jackson ObjectMapper for communication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;[ ... ]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That code uses the basic completion API, not the chat completion API endpoint at api.openai.com/v1/chat/completions. Can you adjust the code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;[ ... ]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Change the return value of send to be a List of instances of inner class "ChatMessage" instead of an array of strings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;[ ... ]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Change the ChatResponse class to match the following JSON:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;[ ... ]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write a sendAsync method that calls the OpenAI.send method asynchronously.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;[ ... ]&lt;/p&gt;

&lt;p&gt;And so on. Eventually, with some manual tweaking, here we are. Just the way I wanted it. Spring Component, very readable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the Vaadin app chat with you
&lt;/h2&gt;

&lt;p&gt;To call the OpenAI's chat endpoint synchronously is simple, add a submit listener to the MessageInput:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addSubmitListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and implement the method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MessageInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SubmitEvent&lt;/span&gt; &lt;span class="n"&gt;submitEvent&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;openAI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;submitEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;     
    &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setItems&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;convertMessage&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That combined with straight-forward conversion from Message to MessageListItem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MessageListItem&lt;/span&gt; &lt;span class="nf"&gt;convertMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MessageListItem&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContent&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                            &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTime&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                            &lt;span class="n"&gt;formatName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRole&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Chatting asynchronously
&lt;/h2&gt;

&lt;p&gt;To make the chat behave more real-time, few steps are needed&lt;br&gt;
Enable websockets by adding &lt;a href="https://vaadin.com/docs/latest/advanced/server-push" rel="noopener noreferrer"&gt;@Push in Application class&lt;/a&gt; and to &lt;br&gt;
use OpenAI.sendAsync instead to return a &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html" rel="noopener noreferrer"&gt;CompletableFuture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; to update the UI in the asynchronous callback, wrapping it into a UI.access call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Push&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;AppShellConfigurator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MessageInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SubmitEvent&lt;/span&gt; &lt;span class="n"&gt;submitEvent&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;openAI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;submitEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;whenComplete&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Lock the Vaadin UI for updates&lt;/span&gt;
        &lt;span class="n"&gt;getUI&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setItems&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;convertMessage&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That was nice. Vaadin components provide a nice familiar look and feel, and you can easily customize them to your own needs. The complete source code is again available on GitHub for your (and AI's) inspiration: &lt;a href="https://github.com/samie/vaadin-openai-chat" rel="noopener noreferrer"&gt;github.com/samie/vaadin-openai-chat&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just update the &lt;a href="https://platform.openai.com/account/api-keys" rel="noopener noreferrer"&gt;openai.apikey&lt;/a&gt; in the &lt;code&gt;application.properties&lt;/code&gt; and you are good to try it yourself. &lt;/p&gt;

&lt;p&gt;Continuing from here, you can guess where this is heading if you already saw my &lt;a href="https://dev.to/samiekblad/always-listening-voice-commands-for-vaadin-web-applications-3bhp"&gt;voice activation tips for Vaadin&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>openai</category>
      <category>vaadin</category>
      <category>java</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Always-Listening Voice Commands for Vaadin web applications</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Tue, 28 Mar 2023 19:31:49 +0000</pubDate>
      <link>https://forem.com/samiekblad/always-listening-voice-commands-for-vaadin-web-applications-3bhp</link>
      <guid>https://forem.com/samiekblad/always-listening-voice-commands-for-vaadin-web-applications-3bhp</guid>
      <description>&lt;p&gt;This small tutorial takes 15 minutes from the start to a working demo.  We use &lt;a href="https://picovoice.ai/platform/porcupine/" rel="noopener noreferrer"&gt;Picovoice Porcupine Wake Word Engine&lt;/a&gt; to enable a &lt;a href="https://vaadin.com/" rel="noopener noreferrer"&gt;Vaadin-based&lt;/a&gt; Java web application.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(If you don't have that much time: clone the &lt;a href="https://github.com/samie/porcupine-web-vaadin-demo" rel="noopener noreferrer"&gt;repo&lt;/a&gt;, create a &lt;a href="https://console.picovoice.ai/" rel="noopener noreferrer"&gt;accesskey&lt;/a&gt; and &lt;a href="https://github.com/samie/porcupine-web-vaadin-demo#running-the-application" rel="noopener noreferrer"&gt;run it&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wake Word Detection is also known as Keyword Spotting, Hotword Detection, Always-Listening Voice Commands, Trigger Word Detection, and Voice Activation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup the Project
&lt;/h2&gt;

&lt;p&gt;The first step is to create a new &lt;a href="https://vaadin.com/flow" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt; Java application project:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Download a new project from &lt;a href="https://start.vaadin.com/?preset=empty" rel="noopener noreferrer"&gt;start.vaadin.com with an empty view&lt;/a&gt;.
&lt;/h4&gt;

&lt;h4&gt;
  
  
  2. Create new Java API for always-on keyword detection and import Picovoice libraries:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.application&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.vaadin.flow.component.UI&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.vaadin.flow.component.dependency.JsModule&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.vaadin.flow.component.dependency.NpmPackage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@NpmPackage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"@picovoice/porcupine-web"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.1.16"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@NpmPackage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"@picovoice/web-voice-processor"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"4.0.5"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@JsModule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./porcupine-integration.js"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Porcupine&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 

    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Porcupine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;picovoiceAccesskey&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;UI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPage&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;executeJs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"window.vaadinPorcupine.key=$0;"&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;picovoiceAccesskey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="no"&gt;UI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPage&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;executeJs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"window.vaadinPorcupine.start()"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="no"&gt;UI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPage&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;executeJs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"window.vaadinPorcupine.stop()"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isStarted&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;started&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the server API available for the rest of the Vaadin Java application.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Create porcupine-integration.js in project’s frontend folder.
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WebVoiceProcessor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@picovoice/web-voice-processor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PorcupineWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BuiltInKeyword&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@picovoice/porcupine-web&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;modelParams&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./porcupine_params.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// a global 'vaadinPorcupine' integration instance is enough&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Starting wake word detection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_worker&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;         
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PorcupineWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BuiltInKeyword&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Computer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keywordDetectionCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;modelParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WebVoiceProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_worker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stopping wake word detection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WebVoiceProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vaadinPorcupine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_worker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;keywordDetectionCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;detection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Detected keyword: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;detection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;voice-wakeword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;detail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;detection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the client side part of the API integrating the in-browser wake word detection library.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Download the Porcupine model  (i.e. Deep Neural Network). From the project frontend folder, run the following to turn the binary .pv model into a base64 string model.
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "const model_params='$( cat porcupine_params.pv | base64 )';\nexport default model_params;\n\n" &amp;gt; porcupine_params.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Get your Picovoice AccessKey
&lt;/h4&gt;

&lt;p&gt;Go to &lt;a href="https://console.picovoice.ai/" rel="noopener noreferrer"&gt;Picovoice Console's dashboard&lt;/a&gt;. Copy your AccessKey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6vm5prhimksd7t3owv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6vm5prhimksd7t3owv3.png" alt="Image description" width="488" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  6. Add a test button to the EmptyView.java to turn the wake word detection on and off:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Porcupine&lt;/span&gt; &lt;span class="n"&gt;porcupine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Porcupine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PICOVOICE_ACCESSKEY"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Start/Stop wake word detection"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;porcupine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isStarted&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="n"&gt;porcupine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;porcupine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;();}&lt;/span&gt;
&lt;span class="o"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the application using your own accessKey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PICOVOICE_ACCESSKEY=your_accesskey_here ./mvnw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Source Code and more examples?
&lt;/h2&gt;

&lt;p&gt;The source code for a fully-working Vaadin demo with Porcupine is available in my &lt;a href="https://github.com/samie/porcupine-web-vaadin-demo" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. I’ve added a custom event and handler example there and later I’ll show how to train and add your own custom wake word using &lt;a href="https://picovoice.ai/docs/tips/choosing-a-wake-word/" rel="noopener noreferrer"&gt;Picovoice Porcupine&lt;/a&gt;…  What should I do next?&lt;/p&gt;

</description>
      <category>picovoice</category>
      <category>vaadin</category>
      <category>java</category>
    </item>
    <item>
      <title>What's the deal with Vaadin add-ons?</title>
      <dc:creator>Sami Ekblad</dc:creator>
      <pubDate>Mon, 30 Jan 2023 12:03:16 +0000</pubDate>
      <link>https://forem.com/samiekblad/whats-the-deal-with-vaadin-add-ons-2f1n</link>
      <guid>https://forem.com/samiekblad/whats-the-deal-with-vaadin-add-ons-2f1n</guid>
      <description>&lt;p&gt;Vaadin is a framework for creating web applications in Java. It grew in the era when Java was the most popular programming language out there. But its benefits and popularity go more profound than the Java language. That is best visible in &lt;a href="https://vaadin.com/directory-beta/" rel="noopener noreferrer"&gt;the way you extend Vaadin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many UI widget libraries out there. Vaadin also offers a consistent set of widgets optimized for "desktop-like" applications. The difference is how those widgets or components are made and behave. &lt;a href="https://vaadin.com/developers" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt; provides three kinds of add-on templates. They are not the only options you have, but they reflect the different use cases and benefits of the framework. Let's have a look at each of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-only add-on
&lt;/h2&gt;

&lt;p&gt;This is the most "Vaadin-native" way of extending Vaadin. In this, the component is entirely composed of existing Vaadin components or other add-ons. It is good for making bigger logical UI composites like CRUDs and "micro-UIs" for e.g., editing certain types of data. Think of "person editor" instead of just a "form". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f18y3acadhm7m8m8q33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f18y3acadhm7m8m8q33.png" alt="Vaadin Server Add-on " width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is close to how you would typically build and divide the whole application UI in Vaadin. The component state resizes fully on the server side, and the framework for you renders the client side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully validated user input &lt;/li&gt;
&lt;li&gt;No need to use HTML, CSS, JavaScript, or TypeScript.&lt;/li&gt;
&lt;li&gt;Easy UI integration to any Java-library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires all data sent to the server&lt;/li&gt;
&lt;li&gt;Not optimal for creating pure UX improvements like animations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Template: &lt;a href="https://github.com/vaadin/addon-template" rel="noopener noreferrer"&gt;addon-template&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Client-server add-on
&lt;/h2&gt;

&lt;p&gt;This unlocks the full potential of the web and Vaadin in your applications. You are building your reusable components and custom elements that implement a particular user experience combined with desired server-side logic. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz03g40pekelt7k43i2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz03g40pekelt7k43i2l.png" alt="Vaadin Client-Server Add-on" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this component architecture, you can build your client-side logic that is combined with server-side logic. The perfect approach both for creating new innovative UI widgets that securely validate user input, or just a simple yet cool presentation-only widgets. You can let the framework take care of all the communication and lifecycle things and focus on the UX. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full control of DOM combined secure server-side data validation&lt;/li&gt;
&lt;li&gt;Utilize all browser APIs to build remarkable UX and styling&lt;/li&gt;
&lt;li&gt;Validate the input on the server side to keep data consistent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Needs knowledge of DOM, custom elements, JavaScript or TypeScript, and CSS. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Template:  &lt;a href="https://github.com/vaadin/client-server-addon-template" rel="noopener noreferrer"&gt;client-server-addon-template&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Extension from 3rd party NPM package
&lt;/h2&gt;

&lt;p&gt;Since Vaadin components on the client side are &lt;a href="https://webcomponents.today/" rel="noopener noreferrer"&gt;custom elements&lt;/a&gt;, you can use any other custom element with Vaadin. The framework gives you standard ways of interacting with the component using DOM events, attributes, and methods already; typically, that is all you need. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhnle0txlm1ijv3725zxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhnle0txlm1ijv3725zxu.png" alt="Vaadin NPM Add-on" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This opens the vast world of &lt;a href="https://www.npmjs.com/search?q=custom%20element" rel="noopener noreferrer"&gt;client-side integrations&lt;/a&gt;. Further combined with a small client-side JavaScript or TypeScript adapter, you can fine-tune the logic to build a Vaadin component with a less server-side state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Java API for any existing custom element from npmjs.com&lt;/li&gt;
&lt;li&gt;Minimal Vaadin-specific code to write and maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client-side-only logic is not secure and needs also server-side validation&lt;/li&gt;
&lt;li&gt;Integrating 3rd party elements might create inconsistent UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Template:  &lt;a href="https://github.com/vaadin/npm-addon-template" rel="noopener noreferrer"&gt;npm-addon-template&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  The right tools for the use case
&lt;/h2&gt;

&lt;p&gt;The goal is to build highly reusable components that utilize both the server and the client where they are the best. This follows the abstraction philosophy behind Vaadin: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server is the place of secure business logic and validations&lt;/li&gt;
&lt;li&gt;Client-side is for building awesome UX and cool visual effects&lt;/li&gt;
&lt;li&gt;Build logical reusable components and widgets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You, whether a web or Java expert, know the best approach. With the Vaadin add-on, you can decide and build and deliver your web components in a highly maintainable way that is best for both worlds: &lt;a href="https://whatwebcando.today/" rel="noopener noreferrer"&gt;Ever evolving APIs&lt;/a&gt; and &lt;a href="https://github.com/akullpp/awesome-java" rel="noopener noreferrer"&gt;critical Java backends&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What are your experiences with reusability and architecture on the UI layer? &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>java</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
