Step-by-step RESTful web service example in Java using Eclipse and TomEE Plus

Published: 01 Jan 2019

Related Videos

https://www.youtube.com/embed/Tpr4UfkX9e4

TheServerSide has published a number of articles on the tenets of effective RESTful web service design, along with examples of how to actually create a cloud-native application using Spring Boot and Spring Data APIs. In this JAX-RS tutorial, we will go back to basics by developing the exact same application, except this time we’ll use standard Java EE APIs and the extended, enterprise version of Tomcat, TomEE Plus, as our deployment target. This step-by-step JAX-RS RESTful web service example in Java using Eclipse and TomEE Plus will get you up to speed on modern web service development techniques in less than 15 minutes. 

Prerequisites

This tutorial uses Eclipse Oxygen as the development environment, the underlying JDK is at version 1.8, and the deployment target is TomEE Plus. You can download TomEE Plus from the project’s Apache home page.

Why are we using TomEE Plus, and not Tomcat or the standard TomEE offering? Sadly, the basic Java web profile, which Tomcat 9 implements, does not support JAX-RS, it does not include the javax.ws.rs.* packages, and without playing with POM files or adding JAR files to the Eclipse project’s \lib directory, RESTful web services simply won’t work. The standard TomEE offering doesn’t include JAX-RS libraries either. On the other hand, the TomEE Plus server does include various enterprise packages, including JAX-RS, so RESTful web services will deploy right out of the box, making this RESTful web services example much simpler.

Step 1: The dynamic web project

The first step in this JAX-RS tutorial is to kick off the dynamic web project creation wizard in Eclipse. 

JAX-RS project creation
This JAX-RS tutorial utilizes a dynamic web project in Eclipse.

When the dynamic web project wizard appears, name the project restful-java, choose Apache Tomcat 8.5 as the target runtime (even though we are using TomEE Plus, not Tomcat), specify 3.1 as the dynamic web module version and choose a minimal configuration for the project. When these options are set, click Finish.

Note that you need to install TomEE Plus prior to doing this JAX-RS tutorial. You can also use any other application server that supports Java EE and JAX-RS for this RESTful web service example in Java using Eclipse.

Where is the web.xml file?

If you look at this project in GitHub (link below), you’ll notice that there isn’t a web.xml file. That makes traditional enterprise developers nervous, but so long as everything is annotated, there’s no need for one in version 3.x of the Servlet and JSP spec. In older REST implementations you would need to configure a Jersey Servlet and perform a REST Servlet mapping, but that is no longer necessary. In this case, TomEE Plus will process all of the annotations on the classes in the Java web project and make RESTful web sevices available accordingly. It should be noted that on some servers, you do need to reference your JAX-RS classes explicility, which you can do through an Application class. That process is addressed in the JAX-RS problems section towards the end.

JAX-RS project settings
Specify project settings for the RESTful web service example in Java using Eclipse.

Step 2: Create the Score class

This restful web service example in Java using Eclipse models a score counter for an online rock-paper-scissors application, so the first requirement is to create a class named Score that keeps track of wins, losses and ties.

package com.mcnz.restful.java.example;
public class Score {
     public static int WINS, LOSSES, TIES;
}

To keep things simple, we won’t add any setters or getters. Furthermore, we are going to make the properties of the Score class static, as that will enable the Java virtual machine (JVM) to simulate persistence between stateless calls to the web service. This approach will enable us to run and test the application on a single JVM. However, you should manage application state in this way only as a proof of concept. It’s better to persist data with Hibernate and Java Persistence API or save information to a NoSQL database, but that is beyond the scope of this JAX-RS tutorial.

Step 3: Code the JAX-RS Service class

A class named ScoreService is the heart and soul of this RESTful web service example in Java using Eclipse. As such, decorate it with an ApplicationPath annotation that defines the base URL of the web service.

package com.mcnz.restful.java.example;
import javax.ws.rs.*;
 
@ApplicationPath("/")
public class ScoreService {  }

This class will contain three getter methods that enable RESTful web clients to query the number of wins, losses or ties. These methods are invoked through an HTTP GET invocation and return the current win, loss or tie count as plain text. As such, these methods each have a JAX-RS @GET annotation, a @Produces annotation indicating they return a text string and a @Path annotation indicating the URL clients need to use in order to invoke the method:

@GET @Path("/score/wins")@Produces("text/plain")
public int getWins() {return Score.WINS;}
    
@GET @Path("/score/losses")@Produces("text/plain")
public int getLosses() {return Score.LOSSES;}
    
@GET @Path("/score/ties")@Produces("text/plain")
public int getTies() {return Score.TIES;}

The increase methods of this JAX-RS tutorial’s ScoreService follow a similar pattern, with the exception of the fact that each method is triggered through an HTTP POST invocation:

@POST @Path("/score/wins")@Produces("text/plain")
public int increaseWins() { return Score.WINS++; }
    
@POST @Path("/score/ties")@Produces("text/plain")     
public int increaseTies() { return Score.WINS++;}
    
@POST @Path("/score/losses")@Produces("text/plain")        
public int increaseLosses() {return Score.LOSSES++;}

The final two methods of the ScoreService class enable users to get the JSON-based representation of the complete score or pass query parameters to the web service to update the static properties of the Score class. Both methods use the /score path, and both produce JSON. But the getScore method is invoked through an HTTP GET request, while the update method is invoked through a PUT.

Just for the record, there is an easier way to return JSON from a RESTful web service than by using the String.format call. You can use @Producer annotations and simply return JavaBeans, but because we are using static variables in our Score class, doing that gets a bit messy. We will save that for a future RESTful web services tutorial with Eclipse.

@GET
@Path("/score")
@Produces("application/json")
public String getScore() {
   String pattern =
      "{ \"wins\":\"%s\", \"losses\":\"%s\", \"ties\": \"%s\"}";
   return String.format(pattern,  Score.WINS, Score.LOSSES, Score.TIES );  
}
 
@PUT
@Path("/score")
@Produces("application/json")
public String update(@QueryParam("wins") int wins,
                        @QueryParam("losses") int losses,
                        @QueryParam("ties")   int ties) {
   Score.WINS   = wins;
   Score.TIES   = ties;
   Score.LOSSES = losses;
   String pattern =
      "{ \"wins\":\"%s\", \"losses\":\"%s\", \"ties\": \"%s\"}";
   return String.format(pattern,  Score.WINS, Score.LOSSES, Score.TIES );  
}

Step 4: Deploy the JAX-RS web service

Now that you’ve coded the JAX-RS tutorial’s ScoreService, it’s time for this RESTful web service example in Java using Eclipse to move into the testing stage. Remember that we are using TomEE Plus as our target server, not Tomcat. Tomcat doesn’t provide built in JAX-RS support.

To test the application, first right-click on the restful Java project, and choose Run As > Run on server. This will deploy the web project and start the Apache TomEE Plus server that hosts the application.

Eclipse JAX-RS deployment
Run the RESTful web services example in Java on Tomcat.

Step 5: Test the JAX-RS web service example

When you deploy the JAX-RS tutorial app, there are a number of different ways to test it. One way is to simply type the URL of the RESTful web service example into a web browser. A call to the following URL will trigger a GET invocation and a JSON string representing the initial score should be displayed:

http://localhost:8080/restful-java/score
JSON string output
Test the JAX-RS tutorial app by invoking it into a web browser.

To test the increaseTies method, run the following two curl commands in a Bash shell:

$ curl -X POST "http://localhost:8080/restful-java/score/ties"
$ curl -X GET "http://localhost:8080/restful-java/score/"

The JSON string returned from the second command indicates that the number of ties has indeed been incremented by one:

{ "wins":"0", "losses":"0", "ties": "1"}

Now, use curl to trigger a PUT invocation with query parameters:

$ curl -X PUT "http://localhost:8080/restful-java/score?wins=1&losses=2&ties=3"

This PUT invocation will return the following JSON string:

{ "wins":"1", "losses":"2", "ties": "3"}

Fixing common JAX-RS problems

In this example, the ScoreService class is annotated with @ApplicationPath. This works fine with TomEE Plus, but on other servers or older implementations, the @ApplicationPath annotation is placed on a separate class that extends the JAX-RS Application class. This often solves the problem of RESTful URLs simply not being recognized and triggering a 404: The origin server did not find a current representation for the target resource error when an attempt is made to invoke them.

 import javax.ws.rs.core.Application;

@ApplicationPath("/")
public class ScoreApplication extends Application {
public Set<Class<?>> getClasses() { return new
HashSet<Class<?>>(Arrays.asList(ScoreService.class));
}
}

On servers where the implementation is Jersey based, the class can be replaced with one that is a bit easier to understand, although it calls on Jersey APIs explicitly, so it will only work with a Jersey based implementation. You just tell it the names of the various packages where JAX-RS annotated web services reside, and it ensures they are loaded:

import javax.ws.rs.ApplicationPath;

import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("/")
public class ScoreApplication extends ResourceConfig {

public ScoreApplication() {
packages("com.mcnz.restful.java.example");
}
}

And of course, you must ensure you are using TomEE Plus and not Tomcat. As was mentioned earlier, a standard Tomcat installation will not run RESTful web services without a JAX-RS implementation added to the \lib directory, Gradle build script or Maven POM.

And that’s a complete, step-by-step JAX-RS RESTful web service example in Java using Eclipse and TomEE Plus.

The full source code for this example can be downloaded from GitHub.

Create a JAX-RS web service with Tomcat, Eclipse

If you used TomEE in an attempt to create a JAX-RS web service and ran into issues, watch this new video that instead uses Tomcat and Eclipse to create this RESTful web service.

Automatically Generate REST and GraphQL APIs From Your Database

by Adrian Machado

Automatically Generate REST and GraphQL APIs From Your Database | Zuplo Blog

Building APIs from scratch takes time, requires extensive testing, and often leads to inconsistencies between your database schema and API endpoints. Automatically generating APIs directly from your database schema eliminates these pain points while reducing development time from weeks to minutes. This approach is particularly valuable for teams building internal tools, prototypes, or any application where rapid development is important.

The ability to generate APIs automatically has transformed how developers build and maintain applications. Instead of writing repetitive CRUD endpoints, converting API requests to CRUD SQL queries, managing documentation, and maintaining consistency between database schemas and API contracts, developers can focus on building features that matter to their users. This article explores the tools and approaches available for generating both REST and GraphQL APIs from various database types.

Table of Contents#

Why Generate APIs From Your Database#

Traditional API development involves writing code to map database operations to HTTP endpoints, implementing authentication, managing documentation, and ensuring data validation. This process is time-consuming and error-prone. Automatic API generation solves these issues by creating standardized endpoints directly from your database schema.

The benefits extend beyond just saving time. Generated APIs automatically stay in sync with your database schema, reducing bugs caused by outdated API endpoints. They often include built-in features like filtering, pagination, and sorting that would otherwise require custom implementation. Many tools also generate API documentation automatically, ensuring it stays current with your schema changes.

REST API Generation Tools#

REST APIs remain the most common choice for web services due to their simplicity and broad support across platforms. Modern tools can generate REST APIs that are both powerful and secure, with features like role-based access control and request validation built-in.

PostgreSQL Solutions#

postgrest

PostgREST stands out as the leading solution for PostgreSQL databases. It turns your database directly into a RESTful API with minimal configuration. The tool automatically creates endpoints for tables and views, supports complex filters, and leverages PostgreSQL’s row-level security for fine-grained access control. If you’d like to see this in action, check out our Neon PostgresQL sample.

Prisma combined with ZenStack offers a more programmatic approach. While requiring more setup than PostgREST, it provides better TypeScript integration and more control over the generated API. This combination excels in projects where type safety and custom business logic are priorities.

MySQL Solutions#

Dreamfactory

DreamFactory provides comprehensive API generation for MySQL databases. It includes features like API key management, role-based access control, and the ability to combine multiple data sources into a single API. The platform also supports custom scripting for cases where generated endpoints need modification.

We also created our own MySQL PostgREST sample if you’d like to have more control over the implementation and hosting.

NoSQL Solutions#

NoSQL databases benefit from tools like PrestoAPI and DreamFactory, which handle the unique requirements of document-based data structures. PrestoAPI specializes in MongoDB integration, providing automatic API generation with built-in security features and custom endpoint configuration.

Hasura, while primarily known for GraphQL, also generates REST APIs. It supports multiple NoSQL databases and provides real-time subscriptions, making it particularly useful for applications requiring live data updates.

Tweet

Over 10,000 developers trust Zuplo to secure, document, and monetize their APIsLearn More

Multi-DB Support#

Some solutions are flexible to handle multiple types of databases. Often allowing you to combine them into a single API. We already mentioned Dreamfactory, but others include ApinizerDirectus, and sandman2.

Managed DB Solutions#

You can’t talk about REST APIs for Postgres without mentioning Supabase‘s excellent REST API they generate over your database using PostgREST.

GraphQL API Generation Tools#

GraphQL APIs offer more flexibility than REST by allowing clients to request exactly the data they need. This effectiveness makes them more popular, especially for applications with complex data requirements.

Postgres GraphQL Solutions#

Hasura

Hasura and PostGraphile lead the PostgreSQL GraphQL landscape. Hasura provides real-time subscriptions and a powerful permissions system, while PostGraphile offers deep PostgreSQL integration and excellent performance for complex queries.

MySQL and NoSQL Solutions#

StepZen and AWS AppSync excel at generating GraphQL APIs for MySQL and NoSQL databases. StepZen simplifies the process of combining multiple data sources, while AppSync provides smooth integration with AWS services and real-time data capabilities.

Other notable mentions:

Other Databases#

Some other databases often include REST or GraphQL API generation as a part of the associated cloud/SaaS offering. This includes:

Making the Right Choice#

For simple projects with PostgreSQL, PostgREST, or Hasura provide excellent starting points. More complex applications might benefit from tools like Prisma or AWS AppSync, which offer greater flexibility and integration options.

Remember that while automatic API generation can significantly speed up development, it’s not a silver bullet. Complex business logic, custom authentication requirements, or specific performance needs might require additional development work. If you do need a more robust solution for building APIs without sacrificing developer productivity – you should check out Zuplo.

Common Questions About API Generation#

Q: How secure are automatically generated APIs? Most tools provide built-in security features like role-based access control and API key management. However, you should review the security features of your chosen tool and implement additional security measures as needed.

Q: Can the generated endpoints be customized? Yes, most tools allow some level of customization through configuration files, middleware, or custom code injection points. If you’d like a fully-customizable experience while still matching your database, check our our article on generating OpenAPI from your database.

Q: What about performance? Generated APIs can be highly performant, especially when using tools that improve database queries. However, complex operations might require manual optimization.

Q: How can one handle complex business logic? Many tools support custom functions, stored procedures, or middleware that can implement additional business logic beyond basic CRUD operations.

Configure linux debian to boot into a fullscreen application

Running kiosk-mode applications with confidence

Configure linux debian to boot into a fullscreen application – Tech Couch

Table of contents

Creating a testing environment
Starting an app in kiosk mode
Fine-tuning chromium for kiosk mode
Handling power saving and blank screen
Using a splash screen during boot
Configuring virtual terminals
Preventing users from escaping the kiosk app
Considerations for touchscreen devices
Device maintenance and security

Linux is often used to build so-called “kiosk” systems; a configuration that will start a fullscreen application right after boot, without any user interaction or login required. This kind of configuration is common for DIY and low-cost devices, but is not as straight-forward as one may assume.

Creating a testing environment

Since testing configurations on a physical device can be cumbersome, this article will use vagrant and virtualbox to create a virtual machine to test the configuration and see it in action. Make sure you have both tools installed, then write the following content into a file named Vagrantfile:

Vagrant.configure("2") do |config|
 config.vm.box = "debian/bookworm64"
 config.vm.provider "virtualbox" do |vb|
   vb.gui = true
   vb.cpus = 2
   vb.memory = "2048"
 end
end

You can now start your virtual machine with

vagrant up

To interact with the virtual machine once it has been created, run

vagrant ssh

to obtain an SSH session as the vagrant user. This user is automatically created for you and already has sudo privileges.

When you are done testing, or want to start over, you can get rid of the virtual machine with

vagrant destroy -f

To get a new clean virtual machine, simply use vagrant up again.

Starting an app in kiosk mode

The guide assumes you have a linux debian installation without any desktop environment installed (if you’re using the vagrant config above, you have exactly that).

Before starting, make sure the packages are up to date

sudo apt update -y && sudo apt upgrade -y

Next install the required packages:

sudo apt install -y xserver-xorg x11-xserver-utils xinit matchbox-window-manager chromium

Here is a brief explanation of what we need each for:

  • X server and tools (xserver-xorgx11-xserver-utilsxinit): Handles the low-level basics for graphical applications, like displays, inputs and rendering.
  • Window manager (matchbox-window-manager): Manages windows on top of xorg, including features like size, placement and responding to fullscreen requests. We chose this window manager specifically because it is extremely lightweight and works well for kiosk mode setups.
  • Kiosk app (chromium): The application we want our system to boot into. Chromium is a simple browser that works well for displaying local or remote websites in kiosk mode, but you could use any other graphical application that has fullscreen support in it’s place.

With these dependencies installed, it is time to configure the kiosk mode starting sequence. This requires a user that the system will automatically log in as, preferrably a newly created one for this specific purpose:

sudo useradd -m -s /bin/bash -p '' kioskuser

This creates a new user called kioskuser. They have a home created because we need to create init scripts there in a moment, use /bin/bash as their shell because we need .bashrc during startup, and have an empty password to allow passwordless login at boot.

The initial virtual terminal and login screen is handled by the getty service. In order to automatically log into our kiosk account at boot, we need to override the default parameters for it. Create the file /etc/systemd/system/getty@tty1.service.d/override.conf with adjusted ExecStart parameters:

sudo mkdir /etc/systemd/system/getty@tty1.service.d
sudo cat > /etc/systemd/system/getty@tty1.service.d/override.conf<< EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin kioskuser --noclear %I $TERM
EOF

Overriding the getty@tty1 service only applies our automatic login for the first virtual terminal tty1 (not others available through ctrl+alt+f2f6, and not remote sessions like SSH). The first blank ExecStart= line is intentional and important, as it clears the previous contents inherited from the main getty service unit we are overriding. If it were missing, our arguments would be appended to the original line, not working as intended. If you named your kiosk user differently, make sure to adjust the last line after the --autologin flag to your custom username.

Now that the kioskuser is automatically logged in as at boot, we can append a small script to the end of their .bashrc file to start the xorg display manager for the tty1 session (and not for subsequent sessions or remote connections):

sudo cat >> /home/kioskuser/.bashrc<< EOF
if [ -z "\$DISPLAY" ] && [ "\$(tty)" = "/dev/tty1" ]; then
  startx
fi
EOF

The last task is to start the window manager and kiosk application once the xorg server is ready. We can simply write those commands to a .xinitrc file, which gets executed once the startx command completes:

cat > /home/kioskuser/.xinitrc<< EOF
matchbox-window-manager &
chromium --kiosk https://tech-couch.com
EOF
sudo chown kioskuser:kioskuser /home/kioskuser/.xinitrc

Note the ampersand & after the first command, ensuring the window manager starts in the background and does not block the chromium process.

Using the --kiosk flag for chromium will automatically start it in fullscreen and disable some UI elements, like tabs and the search bar the top. Instead of a remote URL, you could also provide a filepath to a local HTML file.

All that is left to do now is reboot the machine to see the configuration take effect:

sudo reboot

The virtual machine should now boot directly into the chromium application.

Fine-tuning chromium for kiosk mode

While supplying the --kiosk flag is a good start, there are other useful flags when running chromium in kiosk mode:

  • --no-first-run suppresses the first-run dialog (asking to sign in / sync profiles)
  • --incognito ensures browsing history, cookies and site data are not stored, since kiosk applications have to use for these features.
  • --disable-restore-session-state prevents chromium from showing a “restore closed tabs” page after unexpected shutdown, like power loss. Since kiosk mode devices are often turned off by unplugging, this feature may interfere with normal operation.
  • --disable-infobars hides infobars like the “Chrome is being controlled by automated test software” message that might appear when instrumenting the browser with WebDriver or debugging tools
  • --disable-pinch disables pinch-to-zoom functionality for touchscreens.

Depending on your use case, you may find some or all of these additional flags useful for chromium kiosk apps.

Handling power saving and blank screen

If left in default configuration, the xorg display manager will turn off the screen after some time of inactivity to save power. This may me desirable for interactive devices that have a keyboard or touchscreen attached, as any form of input will wake up the system again. For passive devices like screens displaying advertisements or informational displays, this behavior is problematic and needs to be prevented.

You can disable these features entirely by combining three xset commands. Simply prepend these lines to the beginning of /home/kioskuser/.xinitrc:

xset s off
xset -dpms
xset s noblank

The first line prevents screen blanking, the second disables dpms (Display Power Management Signaling) and the last one prevents any screen saver activation.

Using a splash screen during boot

The default boot process is very noisy, printing numerous colored lines of logging output to the screen. For many devices running kiosk mode, this boot sequence may concern unsuspecting users, so hiding most of the noise is a good idea.

This can be done by using a plymouth splash screen theme, which first needs to be installed:

sudo apt install -y plymouth plymouth-themes plymouth-x11

Plymouth comes packaged with several themes by default, you can get a list of them with

sudo plymouth-set-default-theme -l

Once you settled on a theme you want, apply it to your environment with

sudo plymouth-set-default-theme -R tribar

The tribar theme is a good default choice here, as it is lightweight enough to load fast but also clean and unintrusive.

The second part to adjust for a cleaner boot experience is the grub configuration. Open /etc/default/grub and set GRUB_TIMEOUT=0 (create it if missing). Next, find the line GRUB_CMDLINE_LINUX_DEFAULT and prepend quiet loglevel=0 splash to it.

For example, if your grub file looked like this initially:

GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="net.ifnames=0 biosdevname=0"
GRUB_CMDLINE_LINUX="consoleblank=0"

it should look like this after modification:

GRUB_DEFAULT=0
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT=" quiet loglevel=0 splash net.ifnames=0 biosdevname=0"
GRUB_CMDLINE_LINUX="consoleblank=0"

Finally, run

sudo upgrade-grub

to save your new configuration. The next reboot should be mostly quiet, with a splash screen or animation covering the remaining boot logging noise.

If you want even more control over the boot animation, you could design your own plymouth theme or show a static image.

Configuring virtual terminals

It is common for linux systems to dynamically provide several virtual terminals after boot. A virtual terminal is essentially a login session, just like the one we used to automatically log in. At any point in time, you may press ctrl+alt+f2 to switch to tty2 (the second virtual terminal), up to ctrl+alt+f6 for the last one.

If you are following along with virtualbox, you need to use the software keyboard from the top menu under input > Keyboard > Soft Keyboard ... to send key combinations to the vm.

Keeping this functionality may or may not be desirable: If end users can send inputs (e.g. a keyboard is connected to the device), they can effectively “disable” the device by switching to a different virtual console (where the kiosk app isn’t visible, and just a text-based login appears). Only users familiar with linux systems will recognize this and be able to amend it, while less experienced people have no choice but to restart the device.

On the other hand, devices that cannot be interacted with directly like informational displays may want to keep this feature enabled to allow operators to quickly debug the device in-place, simply by attaching a keyboard to it and switching to another tty to log in and issue commands.

If you want to disable virtual terminals and leave only the session used for the kiosk mode app, you need to find these lines in /etc/systemd/logind.conf and make sure they are not commented out and have the correct values:

NAutoVTs=1
ReserveVT=0

The first line defines how many total virtual terminals are automatically generated, the second defines a reserved login virtual terminal that is always available (where value 0 disables this feature).

Your changes will take effect after the next reboot.

Preventing users from escaping the kiosk app

Simply starting an app in fullscreen may initially look like everything works as intended, but as soon as users have access to input, they may be able to break out of the kiosk app. This can take many forms, for example alt+f4 to close the kiosk app, ctrl+tab to try and switch to a different program or application-specific shortcuts like ctrl+w to close the current tab in chromium (thus closing the browser, since only one tab was open).

The only reliable way to prevent these issues is to unbind all ctrl and alt keys (left and right), as this leaves their key combinations inaccessible. You can unbind them using xmodmap when kioskuser starts their xorg session by prepending this command to the beginning of /home/kioskuser/.xinitrc:

xmodmap -e "keycode 37 = NoSymbol" \
        -e "keycode 64 = NoSymbol" \
        -e "keycode 105 = NoSymbol" \
        -e "keycode 108 = NoSymbol"

This solution has the added benefit that it only affects the kiosk app session on tty1, other sessions (e.g. over SSH) and other users remain unaffected.

After a reboot, feel free to try all key combinations to ensure you cannot escape the kiosk environment with them anymore.

If you are following along with virtualbox, you need to use the software keyboard from the top menu under input > Keyboard > Soft Keyboard ... to send key combinations to the vm.

Considerations for touchscreen devices

Before adding touchscreen features to this setup, take a moment to consider if debian is the right platform for your needs. Handheld-optimized distributions like android will be significantly easier to set up for touch devices, and you are less likely to find edge-case issues or hardware incompatibilities with them.

The primary need of a touchscreen device is a soft keyboard (an on-screen software keyboard) to allow users to input keystrokes without a physical keyboard connected. There are many options here, from lightweight choices like matchbox-keyboard (a good choice for the matchbox-window-manager) to more complex and feature-rich alternatives like florence or onboard. The choice is mainly up to your device’s needs and personal preference, but be warned that setting up dynamic focus (automatically show/hide the keyboard when text input is selected) may be tricky on some combinations.

Since most touch devices do not show a cursor to the user, you likely want to hide the one on your touch device as well. An easy solution is to use unclutter with an idle timeout of 0 seconds, to hide the cursor most of the time. This setup may cause a flickering cursor on mouse move for some touchscreens, in which case you need to use a more complex solution like creating and installing a cursor theme with an invisible cursor icon.

Lastly, your kiosk application may behave unexpectedly with touch devices. The chromium browser for example, includes support for some touch gestures, like navigating with swiping motions or zooming with a pinch gesture. These can be easily disabled with flags --overscroll-history-navigation=0 and --disable-pinch, respectively. Spend some time checking if and what touch gestures your application supports, and consider how that may negatively impact the user experience.

Device maintenance and security

After setting up your device, you may think about how to maintain it in the future. Running an SSH server on the device to remotely manage it may seem like a good idea at first, but be careful to either disable SSH login for the kioskuser account or limit logins to key-only authentication, as otherwise you provide remote access to anything in the device’s network through the passwordless kiosk application user.

On the topic of security, you should make it a point to treat all your deployed/installed devices as hostile. Even if no remote access is possible over SSH, an attacker could simply plug the storage disk into one of their machines and freely read/change any files on the system. Do not store credentials on the device (like S3 storage bucket logins or FTP credentials), not even hardcoded into a compiled binary (strings can be stripped out of executables easily with the strings command or debugging software).

If your device needs authenticated access to remote infrastructure, for example to forward orders or reports, consider deploying a purpose-built REST API with limited access for the devices, and provide different API tokens for each device to easily lock specific ones out of the service should they start behaving undesirably or their token becomes compromised. Audit logging and rigid monitoring/alerting are a valuable addition of such an API in production environments.