Dopo anni in cui l’Open Source ha trovato grosse difficoltà a diffondersi, in parte per scetticismo, in parte per la scarsa struttura dell’offerta stessa, ora possiamo dire che questo modello ha finalmente raggiunto una maturità tale per cui sta cominciando a essere impiegato dalle aziende sempre più frequentemente, con risultati soddisfacenti.

A confermarlo il report pubblicato da NextValue sull’offerta Open Source in Italia secondo cui, almeno per quanto riguarda l’Italia, “ben il 73% delle aziende del panel utilizza software Open Source, mentre l’85% è familiare con soluzioni Open Source e conosce altre aziende che le hanno adottate. Le soluzioni di Open Source sono sempre più utilizzate per guidare il processo di innovazione delle core operations e la loro adozione in processi mission-critical è ormai prevalente.”

open source report

Una parte dei business manager rimane però ancora restia: i motivi? Secondo il recente studio, “spesso le aziende italiane faticano a trovare personale preparato per la gestione di progetti Open Source, si pensi per esempio a programmatori Java esperti. Questa tendenza si accompagna spesso con il fraintendimento che le soluzioni Open Source siano non assistite, cioè erogate senza nessun servizio di consulenza, formazione, assistenza e supporto.”

Ma è davvero fondata questa affermazione?

Piattaforma open source significa più esperti, meno errori

L’Open Source è nato con l’idea di sviluppare un software aperto grazie alla collaborazione di diversi esperti IT per modificare e progettare una soluzione avanzata e all’avanguardia partendo solamente dalle fondamenta, cioè dal codice sorgente.

“Give enough eyeballs, all the bugs are shallow” Eric S. Raymond

La frase qui sopra, appartenente a uno dei più influenti informatici statunitensi, racchiude il senso del modello open e della community che sta alla base. “Dato un numero sufficiente di occhi, tutti i bug vengono a galla” significa che per ogni piattaforma esiste una comunità di volontari esperti IT che contribuisce allo sviluppo di progetti e soluzioni a partire dal codice sorgente. Questo significa appunto che gli errori di scrittura sono molto più facilmente individuabili e quindi velocemente risolvibili.

Secondo i sostenitori dell’Open Source Software, un elemento che gioca a favore della qualità del software aperto consiste proprio nelle forti motivazioni di sviluppatori e utilizzatori, che scelgono liberamente di partecipare alla correzione di errori.

Open Source Community sta per qualità e innovazione

Un sinonimo di software open è software libero, concetto che forse ne chiarisce maggiormente l’essenza. Tale modello nasce per essere fruito apertamente. La community stessa nasce come atto spontaneo, senza nessun tipo di vincolo, per la condivisione della conoscenza ed esperienza di ogni sviluppatore. Rientra quindi tra le probabilità che un programmatore possa sospendere in qualsiasi momento il progetto che stava seguendo per contribuire a svilupparne altri.

Quando si parla di titubanza nei confronti del mondo open si fa riferimento più che altro all’affidabilità della stessa community. Quello che si pensa comunemente è che per lo stesso concetto secondo il quale la community open source nasce per essere collaborativa e aperta, nel senso che chiunque – con le competenze adeguate – può partecipare e intervenire, chiunque possa anche decidere di dedicarsi ad altro o interrompere all’improvviso. A questo punto si pensa a un arresto totale del progetto, con conseguente perdita di tempo e denaro. Di questo, nello specifico, si lamentano le aziende che decidono di affidarsi al modello open per portare a termine un progetto, un’applicazione etc.

“Il panel interrogato sembra dirci “più servizi”, una richiesta che i provider in quest’area dovrebbero senz’altro tenere in considerazione, sia nella definizione del loro portafoglio Open Source, sia nella pianificazione ed esecuzione della loro strategia di go-to-market. Non a caso, non solo i principali provider di tecnologie Open Source, come RedHat, hanno un modello di business centrato su servizi di formazione e supporto, ma anche molti dei principali System Integrator internazionali hanno esteso le loro competenze e il loro portafoglio servizi al nuovo paradigma, realizzando in molti casi specifiche practice per aiutare i loro clienti a operare con un’architettura open.”

Supporto: è questo quello che chiedono le aziende che si approcciano all’open source e i provider più importanti stanno già andando in questa direzione. Nel caso specifico di Red Hat, leader indiscusso a livello mondiale nella fornitura di soluzioni open source per l’IT di livello enterprise, il vero punto di forza sta appunto nella collaborazione attiva con le imprese che non hanno risorse IT sufficienti per sviluppare un progetto open. Vengono dunque proposti pacchetti studiati ad hoc, dalla formazione alla certificazione, volti proprio a supportare a 360 gradi le figure aziendali che richiedono assistenza.

È il caso anche di Holon platform che collabora con l’azienda vendor con lo scopo di garantire formazione e supporto in caso venga richiesto e nel momento in cui la supervisione  e cooperazione della community dovesse venir meno.

Holon platform offre alla community documentazione e tutorial, programmi di formazione, assistenza e supporto, grazie a un team di esperti in grado di prendersi in carico del progetto di sviluppo di clienti nelle fasi di:

  • analisi delle attività
  • realizzazione del prototipo
  • sviluppo del progetto
  • delivery dell’applicazione software
  • supporto nel post-delivery

Per le aziende che hanno bisogno di supporto, il team di Holon prevede un sostegno tecnico attraverso un patto di supporto alle imprese. In questo modo si ha la certezza di portare a compimento il progetto, senza obblighi di vincoli.

Collaborazione fra community e provider? Un valore aggiunto

Il segreto sta quindi nella collaborazione, non solo fra esperti sviluppatori, ma anche fra azienda e community, un rapporto che può migliorare ancora di più il livello di qualità del software finale. La presenza infatti dell’azienda fornitrice è un valore aggiunto, perché significa anche contribuire alla crescita, alla maturità e al successo del modello open. Ma soprattutto significa maggiore affidabilità dei prodotti, dunque minori preoccupazioni, più certezze e rassicurazioni per i clienti.

 

 

 

 

Red Hat is the world’s leading provider of Open Source, enterprise IT solutions. We share the Red Hat’s mission to make the Open Source software a trusted and reliable asset for enterprise organizations, believing in the Open Source ecosystem as the most effective way to innovation.

For this reason, we’re pleased to announce that the Holon Platform has been certified for the Red Hat JBoss Middleware and has been published in the Red Hat Certified Product Catalog.

You can learn more here.

We intend to go on in the Red Hat partnership process, to achieve more certifications and provide our contribution to the Open Source growth in the enterprise world.

Follow us to be informed about next steps.

You can explore two examples of a simple web application, created with the Holon Platform and ready to be deployed on Red Hat JBoss EAP, on GitHub:

Multi-tenancy is a fundamental architecture which can be used to share IT resources cost-efficiently and securely in cloud environments, in which a single instance of software runs on a server and serves multiple tenants.

It’s been years since we first heard about it; it came out again riding the wave of cloud computing, so we can assume that multi-tenancy is a consolidated architecture and the benefits in terms of maintainability and costs are well known.

APIs are the backbone of a distributed cloud architecture, so building a multi-tenant API is the natural aftermath in this scenario.

In this article I will show you how to build a simple multi-tenant RESTful API using Java in a quick and easy way, dramatically reducing the configuration code amount required by the most frequently adopted solutions.

A multi-tenancy implementation may be cumbersome

The first impression, looking for multi-tenancy implementation strategies, is that there’s no specific standard and no well defined best practices.

One of the most common solution is relying on Hibernate to implement multi-tenancy behaviour at DataSource level. Hibernate requires two interfaces to be implemented: CurrentTenantIdentifierResolver to resolve what the application considers the current tenant identifier and MultiTenantConnectionProvider to obtain Connections in a tenant specific manner. This could be tricky and not so trivial, possibly leading to boilerplate code rising.

And finally, last but not least, this solution is only suitable for a JPA implementation, since Hibernate is directly used as tenant resolver and connection router.

Furthermore, the Spring Framework is often used in conjunction with Hibernate to build a multi-tenant API or application. One of the most useful aspects of the core Spring architecture is the availability of scopes: in a multi-tenant scenario, sooner or later you’ll surely miss a tenant scope, which is not available out-of-the-box. Creating a custom scope in Spring is not trivial, it requires a deep knowledge of the Spring architecture and a lot of code to implement and register the new scope.

You can check the Spring+Hibernate version of the example shown in the second part of this article here: https://github.com/fparoni/spring-boot-hibernate-multitenant-jaxrs-api.

The easy way

Now let’s streamline the process, in order to highly simplify the multi-tenancy setup and to cut down the required configuration effort, getting rid of all the boilerplate code.

We will use the Holon platform to achieve this goal, relying on the platform’s native multi-tenant support.
I’ll show you a working example of a simple RESTful API, using the following Holon platform’s components and configuration facilities:

  1.  The TenantResolver interface to provide the current tenant identifier
  2. A tenant Spring scope provided out-of-the-box, automatically configurated and registered when using Spring Boot
  3.  The Holon Platform JDBC datastore module to use JDBC for multi-tenant data access
  4.  The Holon Platform JAX-RS module to easily create a RESTful API using Java

The complete example is available on GitHub: https://github.com/holon-platform/spring-boot-jaxrs-multitenant-api-example.

Our RESTful API implements the CRUD operations to manage a simple Product, providing methods to create, update, delete and query the product data.

The multi-tenancy architecture of this example is organized per schema: each tenant is bound to a separate database schema and will access data through a different Java DataSource.

Each time an API request is performed we need to know the caller tenant identifier, to correctly route the persistence operations to the right database schema.

The three most common ways to provide the tenant identifier are:

  1. Providing the tenant identifier as a URL part, for example using a query parameter
  2. Using a custom HTTP request header
  3. Using JWTs to provide the tenant identifier as a JSON token claim

We will use the second option, through a custom HTTP header called ‘X-TENANT-ID’.

An example to use JWT to provide the tenant identifier is available in the jwt branch of the GitHub example repository.

Besides the Holon platform components listed above, for our example we will use:

  1. Spring Boot for application auto-configuration and to create a runnable jar with an embedded servlet container
  2. The H2 in-memory database for data persistence
  3. JSON as data exchange format

The Holon Datastore API will be used to access data in a technology-indipendent way (using JDBC in this example is only a matter of configuration) and the Holon Property model to define the product data model. Check to Holon reference documentation for detailed information.

The result will be a fully working example made of just three Java classes: now let’s build our example step by step.

Step 1: Project configuration

First of all, we will use Maven for project configuration and dependency management.

We’ll use the Holon Platform BOM (Bill of Materials) to easily obtain the dependencies we need.

 

<properties>
  <holon.platform.version>5.0.6</holon.platform.version>
</properties>

<dependencyManagement>
		<dependencies>
			<!-- Holon Platform BOM -->
			<dependency>
				<groupId>com.holon-platform</groupId>
				<artifactId>bom</artifactId>
				<version>${holon.platform.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
</dependencyManagement>

The Holon JDBC Datastore Spring Boot starter is used to automatically configure the data access layer:

<!-- Holon JDBC Datastore with HikariCP starter -->
<dependency>
	<groupId>com.holon-platform.jdbc</groupId>
	<artifactId>holon-starter-jdbc-datastore-hikaricp</artifactId>
</dependency>

Likewise we declare the Holon JAX-RS Spring Boot starter dependency to use and auto-configure Jersey as JAX-RS implementation.

<!-- Holon JAX-RS using Jersey -->
<dependency>
	<groupId>com.holon-platform.jaxrs</groupId>
	<artifactId>holon-starter-jersey</artifactId>
</dependency>

At last, the H2 database dependency:

<!-- H2 database -->
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<version>1.4.196</version>
</dependency>

To configure our Spring application we will use an application.yml configuration file.

We simulate that two tenants are available, the first bound to a schema named tenant1 and the second associated to a schema named tenant2. So we need two DataSource instances, one for each schema.

Auto-configure more than one DataSource with the default Spring Boot DataSource auto-configuration is not possible without writing additional code. For this reason, we’ll rely on the Holon platform DataSource auto-configuration facilities to define the two tenant DataSources, through a holon.datasource configuration property structure like this:

holon:
  datasource:
        tenant1:
      		url: "jdbc:h2:mem:tenant1"
      		username: "sa"
    	tenant2:
      		url: "jdbc:h2:mem:tenant2"
      		username: "sa" 

By default, for each auto-configured DataSource, a Holon JDBC Datastore is created too. Each DataSource/Datastore pair will be available as a Spring bean and qualified with the name specified through the holon.datasource.* properties. For example, the DataSource bound to the first tenant can be injected in a Spring component using the @Qualifier(“tenant1”) annotation.

To be up and running at startup we will use a couple of .sql file to create a sample table named ‘product’ and one row for each tenant.

Step 2: Writing the code

As I said before we need just 3 Java classes.

1. Application definition and configuration

First of all, we create the Application class, which acts as the Spring Boot application entry-point and as the main Spring configuration class:

@SpringBootApplication
public class Application {

	@Bean
	@RequestScope
	public TenantResolver tenantResolver(HttpServletRequest request) {
		return () -> Optional.ofNullable(request.getHeader("X-TENANT-ID"));
	}

	@Bean
	@ScopeTenant
	public Datastore datastore(BeanFactory beanFactory, TenantResolver tenantResolver) {
		// get current tenant id
		String tenantId = tenantResolver.getTenantId()
				.orElseThrow(() -> new IllegalStateException("No tenant id available"));
		// get Datastore using tenantId qualifier
		return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Datastore.class, tenantId);
	}

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

We have configured two Spring beans:

  • A TenantResolver implemetation to provide the current tenant identifier and to enable the Holon platform tenant scope. In this example, we want to use a custom HTTP header named “X-TENANT-ID” to obtain the tenant identifier from the caller. We need the current HttpServletRequest to obtain the header value, so we declare the TenantResolver as request-scoped and rely on Spring to obtain a reference to the current request.
  • A tenant-scoped Datastore is correctly retrieved by Spring through the tenant identifier which acts as bean qualifier.
    The two Datastore instances have already been configured by the Holon Platform Spring Boot autoconfiguration facilities, using the two DataSources declared through the holon.datasource.* configuration properties. The current tenant-related Datastore is retrieved using the bean qualifier which matches with the current tenant id, provided by the TenantResolver interface.

2. The data model

The Product interface represents our data model:

/**
 * Product model
 */
public interface Product {

	static final PathProperty<Long> ID = PathProperty.create("id", Long.class);

	static final PathProperty<String> SKU = PathProperty.create("sku", String.class);

	static final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);

	static final PathProperty<String> CATEGORY = PathProperty.create("category", String.class);

	static final PathProperty<Double> UNIT_PRICE = PathProperty.create("price", Double.class)
			// not negative value validator
			.validator(Validator.notNegative());

	// Product property set
	static final PropertySet<?> PRODUCT = PropertySet.of(ID, SKU, DESCRIPTION, CATEGORY, UNIT_PRICE);

	// "products" table DataTarget
	static final DataTarget<String> TARGET = DataTarget.named("products");

}

The Holon platform Property model is used to define the product data model. Deepening this topic is out of the scope of this article, check the official Holon documentation for further details.

3. The API endpoint

The ProductEndpoint class represents the JAX-RS resource that will provide the API CRUD, using JSON as data exchange format. The class is annotated with @Component to make it available as a Spring component and we rely on the Holon platform Jersey auto-configuration facilities to automatically register the endpoint in the JAX-RS application:

@Component
@Path("/api")
public class ProductEndpoint {

	@Autowired
	private Datastore datastore;

	/*
	 * Get a list of products PropertyBox in JSON.
	 */
	@GET
	@Path("/products")
	@Produces(MediaType.APPLICATION_JSON)
	public List<PropertyBox> getProducts() {
		return datastore.query().target(TARGET).list(PRODUCT);
	}

	/*
	 * Get a product PropertyBox in JSON.
	 */
	@GET
	@Path("/products/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response getProduct(@PathParam("id") Long id) {
		return datastore.query().target(TARGET).filter(ID.eq(id)).findOne(PRODUCT).map(p -> Response.ok(p).build())
				.orElse(Response.status(Status.NOT_FOUND).build());
	}

	/*
	 * Create a product. The @PropertySetRef must be used to declare the request
	 * PropertyBox property set.
	 */
	@POST
	@Path("/products")
	@Consumes(MediaType.APPLICATION_JSON)
	public Response addProduct(@PropertySetRef(Product.class) PropertyBox product) {
		// set id
		long nextId = datastore.query().target(TARGET).findOne(ID.max()).orElse(0L) + 1;
		product.setValue(ID, nextId);
		// save
		datastore.save(TARGET, product);
		return Response.created(URI.create("/api/products/" + nextId)).build();
	}

	/*
	 * Update a product. The @PropertySetRef must be used to declare the request
	 * PropertyBox property set.
	 */
	@PUT
	@Path("/products/{id}")
	@Consumes(MediaType.APPLICATION_JSON)
	public Response updateProduct(@PropertySetRef(Product.class) PropertyBox product) {
		return datastore.query().target(TARGET).filter(ID.eq(product.getValue(ID))).findOne(PRODUCT).map(p -> {
			datastore.save(TARGET, product);
			return Response.noContent().build();
		}).orElse(Response.status(Status.NOT_FOUND).build());
	}

	/*
	 * Delete a product by id.
	 */
	@DELETE
	@Path("/products/{id}")
	@Consumes(MediaType.APPLICATION_JSON)
	public Response deleteProduct(@PathParam("id") Long id) {
		datastore.bulkDelete(TARGET).filter(ID.eq(id)).execute();
		return Response.noContent().build();
	}

}

Note that we injected the Datastore instance to perform data access operations: thanks to tenant scope with which the Datastore bean is declared, we’ll obtain the right Datastore instance according to the current tenant identifier.

Run the application

The spring-boot-maven-plugin will create a runnable jar to have our application up and running using our JRE. We can run application using this maven command:

mvn spring-boot:run

Application is running under Tomcat, listening to port 8080. We can test our endpoint using Postman application and making GET or POST request to the path we’ve defined before. Let’s test the /products operation using both tenants. Setting the X-TENANT-ID header to tenant1 will produce the following result:

Using tenant2 as the X-TENANT-ID header value, the result is:

Summary

We’ve seen how to setup a multi-tenant API quickly, highly reducing the configuration effort and boilerplate code that is often required for this kind of task.
The multi-tenant architecture that we’ve used in our simple API implementation can of course be leveraged for other, more complex, services or applications.
The source code of the application can be found at https://github.com/holon-platform/spring-boot-jaxrs-multitenant-api-example.

It is fundamental, if not essential, for companies to have innovative solutions that can automate and optimize all the activities’ management, so that to be able to answer to a need and/or to deliver services efficiently and in short times.

Companies are in a very fast-paced and competitive environment, where the only solution to keep up with today’s market is to employ software solutions that allows a general optimization starting with the basics, namely the single business departments.

But how to choose an innovative and intuitive software that could allow a real saving in time and money, a sales increase and other advantages?

We can say, in a way, that a big help arrives directly from the future: let’s go for open source! Let me clarify, Open Source doesn’t just mean low-cost – probably the most common feature for which it is used – but it also means quality, innovation and soundness, these are all characteristics able to make a software solution at cutting edge.

What I want to do today is to list a number of reasons and advantages to explain why open source will be the future of IT! Let’s start!

  • Low-cost: it’s obviously the first reason why companies rely on open source and it remains – economically speaking – an important advantage and a criterion to be taken into consideration, that’s why I think we should talk about this subject for first. Let’s start saying that open source doesn’t mean free, as many could think. What I mean is, cost are zero if a company has the sufficient resources to develop in detail a software solution starting from the source code; but if a company doesn’t have an IT department? A company can rely on vendor, it means cost will be not zero, however minimum. For example, Holon Platform allows to use its source code for free but our development team is able to undertake in order to to provide support. This represents a cost, but really minimum if compared to a developed software, ready to use

 

  • Community: an added value of open source is to be able to rely on specialists with technical expertise, that can help to develop a project or a program in an agile and innovative way. For the same concept according to which “many hand make light work”, community drives innovation and brings added value. Community is open source platforms backbone, because open source will be short-lived without collaboration and knowledge sharing. Open Source= Community!

 

  • Agility and flexibility: today the key element to be competitive is agility, the speed with which we are able to deliver a service or meet a specific need. Market travels at the speed of light, so who doesn’t keep up with, falls behind – without any possibility of repêchage . Hard, sad truth but still truth. The secret to be on the fast track is becoming agile, which are exactly the characteristic and value of open source platforms. Open source means agility because it allows to make changes quickly; so it means flexibility and integration thanks to the fact that it adapts easily to new needs but also to other software; therefore means modularity and extensibility because it is open to different needs of those who use it, allowing to use certain components rather than others. This is a precious value, for example, for small-medium sized companies that have a limited budget but an IT department with sufficient technical knowledge to use and to work with an open platform

 

  • No more lock-in: if once vendor lock-in was considered an advantage, because it meant to have as a guarantee support and solidity of a great producer, now it is seen as a disadvantage, not to say a “trap”. Nobody likes to have too “tight” constraints with vendors because it is synonymous with closure, no innovation and substantial costs, especially when a company decides to rely on other vendors or integrate its own software with other IT solutions. There is a good new: thanks to open source, this lock-in is no more binding, since there is more freedom of source code amendment and re-use and not many limitations – even if vendor is always there for support and advice (as in the case of Holon Platform)

 

  • Security: It still exists the idea that open source doesn’t give any IT safety guarantee: true or false? False! On the contrary, the fact that community users and the same producers can intervene means to rely on a safe, solid and quality product because it is revisited, corrected and “fixed” by IT specialists. This is confirmed by data: according to a “Future of Open Source” survey, 72% of respondents said to have chosen an open source platform for IT security reasons.

An open source platform is all this: flexibility, scalability and agility in responding to change and new needs. This last aspect is a fundamental value in today’s dynamics, don’t you think?

Information security is one of the most importart concerns facing an increasily connected world. And since APIs are the glue of the digital landscape, API security is more important than ever.

In this scenario, user identity and access management are core concepts to deal with. In API architectures, in particular with the emerging microservices approach, the real challenge is to enable a strong security and identity management support while keeping efficiency and reliability.

In a microservices environment, the services are scoped and deployed in multiple containers in a distributed setup, and the service interactions are frequent and remote, mostly over HTTP. In this distributed and stateless world, even the user identity has to be managed in a distributed and stateless way.

What we need here is a solution that allows reliable user identity management and authorization checking without additional overhead, and using the JSON Web Tokens (JWT for short) can be the answer.

Quoting from the jwt.io introduction, a JWT can be defined as follows:

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.

In a world where service-to-service communication security is a real concern, using a JSON Web Token offers many advantages:

  • Performance and easy distribution: The JWT representation is compact, and thanks to its small size, the trasmission of a JWT token is fast and can be effectively performed using the HTTP protocol, for example using a URL, POST parameter, or inside a HTTP header.
  • Efficiency: A JWT token is self-contained, carrying in its payload all the identity information (including authorizations), avoiding the need to perform additional calls (for example a database query) to obtain user information.
  • Simplicity and standardization: JWT is an open standard and uses JSON to represent the identity information, a widely used and supported data format.
  • Granular security: With JWT you can provide fine grained resource access control.
  • Debuggability: JSON Web Tokens can be easily inspected, differently for example from an opaque API key.
  • Expiration control: A JWT supports an expiration time, easy to set and control.
  • OAuth2 compliance: OAuth2 uses an opaque token that relies on a central storage. You can return a JSON Web Token instead, with the allowed scopes and expiration.

Ok, now let’s get our hands dirty

I’ll take the simple API application example of my previous article, Spring Boot, Jersey, and Swagger: Always Happy Together, as a starting point to show you how to use the Holon Platform to secure API operations using JWT. The Holon Platform JWT support relies on the jjwt library to encode, decode and verify the JWT tokens.

The source code of the updated example API application can be found on GitHub, in the jwt branch of the spring-boot-jaxrs-swagger-api-example repository: https://github.com/holon-platform/spring-boot-jaxrs-swagger-api-example/tree/jwt.

In a typical scenario, the JSON Web Tokens emission, interchange and consumption process involves at least three actors: the issuer, the protected resource and the client.

1. The JWT issuer

The issuer is in charge to issue a JSON Web Token as a response to a valid authentication request. This step can include the user account credentials validation.

The produced JSON Web Token can contain detailed user information, including for example user profile information and authorizations, in addition to system and validation related data, such as the issuer name and the expiration time. These information are called claims in the JWT terminology, and they constitute the JWT payload. In addition to the set of reserved claims, you can provide a number of custom claims to represent the user identity and account information.

The issuer role can be easily intepreted by a OAuth2 UAA (User Account and Authentication) server, which returns a JWT instead of an opaque and randomly generated token.

In this example, we’ll simulate the JSON Web Token generation by a UAA server, using the Holon Platform authentication and authorization APIs from a JUnit test class. The Holon Platform authentication architecture relies on the Authentication interface to represent the authenticated principal (which can represent an user but also another entity). The Authentication structure supports permissions to represent the authorization grants and generic parameters to specify the account details.

To build a JSON Web Token from an Authentication, the JwtTokenBuilder class can be used. This builder will translate the Authentication permissions and parameters into JWT claims.

The JWT token builder needs to know which kind of token to build (signed or not), which algorithm has to be used to sign the token and any additional reserved claim (such as the token unique id, the issuer name and the expiration time) to add to the token itself before encoding it. This information can be collected and provided to the builder using the JwtConfiguration interface and the Holon Platform Spring Boot support can be used to build a JwtConfiguration using the application configuration properties.

To enable the Holon Platform JWT support, we just have to add the holon-auth-jwt artifact dependency to the dependecies section of our project’s pom:

<!-- Holon JWT support -->
<dependency>
  <groupId>com.holon-platform.core</groupId>
  <artifactId>holon-auth-jwt</artifactId>
</dependency>

Now we can use a set of Spring Boot configuration properties, with the holon.jwt prefix, to auto-configure a JwtConfiguration instance which will be made available as a Spring component. The list of all the configuration properties is available here.

In this example, we want to setup the issuer name to be used, the expiration time and the signature algorithm to use. For the sake of simplicity, in this example we’ll use a symmetric signing algorithm (HMAC with SHA-256), so we only need a single secret key, which has to be shared between the parties.

So you want to modify the application.yml configuration file adding the following configuration properties:

holon:
  jwt:
    signature-algorithm: HS256
    sharedkey-base64: eWGZLlCrUjtBZwxgzcLPnA
    expire-hours: 1
    issuer: example-issuer

The expire-hours property is used to specify an expiration time of one hour, but other configuration properties are available to use a different unit of time (for example expire-minutes, expire-seconds, expire-ms).

From now on, we can simply obtain the JwtConfiguration which represents these configuration properties as an injected dependency.

So, let’s see an example about a JSON Web Token generation from an Authentication object (you can find more examples in the ApiTest unit test class of the source repository):

@Autowired
private JwtConfiguration jwtConfiguration;

@Test
public void jwtBuilder() {
  Authentication authc = Authentication.builder("testUser") // principal name
    // permissions
    .permission("ROLE1").permission("ROLE2")
    // user details
    .parameter("firstName", "Test")
    .parameter("lastName", "User")
    .parameter("email", "test@holon-platform.com")
    // build the Authentication 
    .build();

  // Build the JSON Web Token using the provided configuration and Authentication
  String jwtToken = JwtTokenBuilder.buildJwtToken(jwtConfiguration, authc, 
    UUID.randomUUID().toString() // Token id claim (jti)
  );
}

The JWT produced in the example above will look like this:

eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MDg3Njc1NzAsImp0aSI6ImNkYzNmMmQ3LWY5MWEtNGExZi1iY2Y0LTM1MGM3OWM2Y2FiYyIsInN1YiI6InRlc3RVc2VyIiwiaXNzIjoiZXhhbXBsZS1pc3N1ZXIiLCJleHAiOjE1MDg3NzExNzAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IlVzZXIiLCJlbWFpbCI6InRlc3RAaG9sb24tcGxhdGZvcm0uY29tIiwiQVRIJHJvb3QiOmZhbHNlLCJBVEgkcHJtcyI6WyJST0xFMSIsIlJPTEUyIl19.4iXLnGHClfVPHF48Bz1aIyoOgsou8Usn2yXf4NUut_M

This token can be easily transmitted using the HTTP protocol, using for example an Authorization header.

See this example for a simple JWT authorization server which issues JSON Web Tokens as the response of a POST invocation providing username and password using the HTTP Basic scheme authorization header.

2. The protected resource

Now it’s time to protected our API operations using JWT.

We’ll leverage again on the Holon Platform authentication architecture, using the Realm security abstraction and the Holon Platform Spring Boot support to allow API resources access only to authenticated clients.

We’ll use HTTP Authorization header with the Bearer scheme to obtain the JWT provided by the client. The JSON Web Token will be validated according to the JWT configuration properties (checking the sign, the issuer name and the expiration time), then the JWT claims can be used to obtain the required user identity information.

We want to implement a simple authorization control too, so the permission claims will be used to obtain the user roles and check if it was granted to access an API operation.

First of all, we have to setup the Holon Platform security Realm, specifying:

  • An AuthenticationTokenResolver to translate the JWT into an authentication token which can be interpreted by the Realm’s authenticators.
  • The Authenticator which the Realm has to use in order to perform the account authentication.
  • An Authorizer, responsible for authorization control. The default Authorizer relies on the Authentication permissions to check the user authorization against the required permissions/roles.

The Realm is configured as a Spring component in the Application class and will be automatically located and used by the Holon Platform authorization and authentication architecture:

@Bean
public Realm realm(JwtConfiguration jwtConfiguration) {
  return Realm.builder()
    // HTTP Bearer authorization schema resolver
    .resolver(AuthenticationToken.httpBearerResolver())
    // JWT authenticator using the provided JwtConfiguration
    .authenticator(JwtAuthenticator.builder().configuration(jwtConfiguration).build())
    // default authorizer
    .withDefaultAuthorizer().build();
}

The JwtConfiguration bean is automatically created using the application confguration properties as seen before.

Finally, we enable the API endpoint protection simply by using the Holon Platform @Authenticate annotation on the JAX-RS resource class:

@Authenticate // require authentication
@ApiDefinition(docsPath = "/api/docs", title = "Example API", version = "v1", prettyPrint = true)
@Api("Test API")
@Component
@Path("/api")
public class ApiEndpoint {

  // content omitted

}

The Holon Platform JAX-RS module automatically setup Jersey enabling the following behaviour:

  • A resource method annotated with @Authenticate is only accessible by a valid, JWT authenticated, client request. If the @Authenticate annotation is declared at class level, all the JAX-RS class methods inherits the authentication setup. If the authentication is not present or not valid, a 401 - Unauthorized status error response is returned.
  • The JAX-RS SecurityContext of an authenticated request returns the Authentication instance associated to the request from the getUserPrincipal() method. The Holon Platform translates back the JWT claims into the Authentication permissions and parameters, so they can be simply accessed from the JAX-RS method.
  • The standard javax.annotation.security.* annotations (@PermitAll, @DenyAll, @RolesAllowed) are enabled to perform role-based resource access control, using the  Authentication permissions provided by the JWT. If authorization control is not successfull, a 403 - Forbidden  status error response is returned.
  • For more advanced authorization controls, the Realm component can be injected in the JAX-RS endpoint to leverage on the Realm API operations using the current Authentication.

For example, to declare that no role is required to access the original /ping operation, the @PermitAll annotation can be used:

@PermitAll // no specific role required
@ApiOperation("Ping request")
@ApiResponses({ @ApiResponse(code = 200, message = "OK: pong", response = String.class) })
@GET
@Path("/ping")
@Produces(MediaType.TEXT_PLAIN)
public Response ping() {
  return Response.ok("pong").build();
}

The ROLE2 authorization is required to access the following API operation, and the JAX-RS SecurityContext is used (injected through the standard JAX-RS @Context annotation) to obtain the authenticated principal name:

@RolesAllowed("ROLE2") // ROLE2 is required
@ApiOperation("Get user name")
@ApiResponses({ @ApiResponse(code = 200, message = "OK", response = String.class) })
@GET
@Path("/user")
@Produces(MediaType.TEXT_PLAIN)
public Response userOperation(@Context SecurityContext securityContext) {
  // get the user principal name from the JAX-RS SecurityContext
  String principalName = securityContext.getUserPrincipal().getName();
  return Response.ok(principalName).build();
}

Finally, we add an API operation which uses the Authentication obtained from the JAX-RS SecurityContext to read the custom JWT claims ("firstName", "lastName", "email") and the Holon Platform Realm for additional authorization control. The example response is the JSON representation of a UserDetails example class:

@Inject
private Realm realm;

@RolesAllowed("ROLE2") // ROLE2 is required
@ApiOperation("Get user details")
@ApiResponses({ @ApiResponse(code = 200, message = "OK", response = String.class) })
@GET
@Path("/details")
@Produces(MediaType.APPLICATION_JSON)
public UserDetails userDetails(@Context SecurityContext securityContext) {

  // the Holon platform Authentication is available as the JAX-RS SecurityContext user principal
  Authentication auth = (Authentication) securityContext.getUserPrincipal();

  // use Realm to perform additional authorization checks
  boolean hasRole1 = realm.isPermitted(auth, "ROLE1");

  UserDetails userDetails = new UserDetails();
  userDetails.setUserId(auth.getName());
  userDetails.setRole1(hasRole1);

  // read the JWT claims, translated into Authentication parameters
  auth.getParameter("firstName", String.class).ifPresent(p -> userDetails.setFirstName(p));
  auth.getParameter("lastName", String.class).ifPresent(p -> userDetails.setLastName(p));
  auth.getParameter("email", String.class).ifPresent(p -> userDetails.setEmail(p));

  return userDetails;
}

3. The client

The last actor is the client, which obtains the JSON Web Token from the issuer and uses it to perform the API calls, providing it as HTTP Authorization header Bearer token.

In a service-to-service communication scenario, the JWT obtained from the original API request can be passed around to perform additional inter-service calls. No additional overhead, such as querying the database, is needed to obtain the identity information, which is encapsulated in the JWT payload.

See the ApiTest class to learn how to use the Holon Platform RestClient to perform RESTful API invocations providing the JWT as Authorization header Bearer token.

API Documentation

In the previous post, we’ve seen how to use the Holon Platform Swagger support to create and provide a standard API documentation. Now we want to complete the API documentation, adding the authorization information.

Let’s suppose to use OAuth2 to obtain the JWT bearer token. In this example, the OAuth2 authorization server is available at the https://example.org/api/oauth2 URL.

Our authorization example roles (ROLE1 and ROLE2) will be represented as OAuth2 scopes.

Using the standard Swagger annotations, we’ll add API authorization definitions in the JAX-RS endpoint class this way:

1. Declare the API security schemes (OAuth2 in this example) using the @SwaggerDefinition annotation, including the available authorization scopes, then declare the authorization scheme (that we called jwt-auth) in the @Api annotation:

@SwaggerDefinition(securityDefinition = @SecurityDefinition(oAuth2Definitions = 
  @OAuth2Definition(key = "jwt-auth", description = "JWT Bearer token", flow = Flow.IMPLICIT,  
                    authorizationUrl = "https://example.org/api/oauth2", 
                    scopes = {
		      @Scope(name = "ROLE1", description = "Test role 1"),
		      @Scope(name = "ROLE2", description = "Test role 2") })))
@Api(value = "Test API", authorizations = @Authorization("jwt-auth"))
// other annotations omitted
public class ApiEndpoint {
  // content omitted
}

2. Declare the authorization scopes (the roles), required to access a specific API operation, in the @ApiOperation annotation. For example:

@ApiOperation(value = "Get protected resource", 
  authorizations = @Authorization(value = "jwt-auth", scopes = @AuthorizationScope(scope = "ROLE1")))
  @ApiResponses({ @ApiResponse(code = 200, message = "OK", response = String.class) })
@RolesAllowed("ROLE1") 
// other annotations omitted
public Response protectedOperation() {
  return Response.ok("protected").build();
}

Thanks to the Holon platform Swagger auto-configuration, the API documentation can be obtained in JSON format from the URL:

http://hostname:9999/api/docs

Using the Swagger Editor to display the API documentation, it will appear like this:

The API authorization scheme can be inspected clicking on the lock icons, for example:

Summary

We’ve seen how JWT can be a lightweight and versatile alternative to other traditional authentication systems, mostly in the stateless API and microservices world, and how the Holon Platform can make its implementation simple and reliable.

The source code of this example API application can be found on GitHub, in the jwt branch of the spring-boot-jaxrs-swagger-api-example repository: https://github.com/holon-platform/spring-boot-jaxrs-swagger-api-example/tree/jwt.

See the previous post to learn how to create the API example application using Spring Boot, Jersey and the Holon Platform JAX-RS module.

The API world. Graphic from Wikimedia

We are on the cusp of the API economy: APIs are everywhere, boosting the digital transformation and disrupting the way we think. So a shifting is required also in the way we develop.

APIs are expected to enable automation, in turn driving efficiency, consistency and cost savings. API quality matters, both for API consumers and implementers. For these reasons, building an API infrastructure from scratch may not be a good idea. It is much easier and more productive to use a good framework or library, often more than one.

From a development point of view, it’s crucial to achieve target whilst ensuring the fulfillment of the following requirements: timing, costs saving and quality. Standardization is one of important way to achieve those goals: to ensure consistency, speed-up development, enable factorization and simplify maintanance.

Furthermore, a very important factor in APIs is a complete and accurate documentation. And best of all, the documentation should be produced and provided in a standard form, to automate the documentation process and to ensure a great overall experience in API consuming.

Even for the simplest API, achieving all these goals and accomplishing all the required tasks may be not so simple for a developer. A minimal infrastructure must be setted up, a set of components has to be selected, configured and enabled to work together. The API implementation stack configuration can be a complex, repetitive and error-prone process. The developer should not take care about the configuration and components integration details, focusing on the business tasks the API has to face and resolve.

It is where standards, frameworks and libraries come to help, taking charge of the API infrastructure setup and configuration and leaving the developer free to focus on the API goals. In one sentence: focus on what matters and forget the tedious stuff.

The Holon Platform can act both as glue for industry-standard libraries and as a catalyst for API development productivity.

In this post, we’ll start from the beginning, showing how to create a simple API service. We’ll focus on project setup and configuration, leaving out the API business logic concerns. In fact, this API will provide a single “ping” operation, which will respond with a pong message.

The building blocks we’re going to use for this project are:

  1. Spring Boot for auto-configuration and execution using an embedded servlet container
  2. Jersey, the reference JAX-RS implementation, to create the API operations using the RESTful paradigm
  3. JSON as data interchange format
  4. Swagger (OpenAPI) for API documentation
  5. Spring Boot Actuator for service monitoring and management
  6. And of course, the Holon Platform JAX-RS module, to leverage on some configuration facilities and automate the API documentation creation and provisioning

The code of this example is available on GitHub: https://github.com/holon-platform/spring-boot-jaxrs-swagger-api-example.

Creating the API application

Maven is used for project setup and dependency management, so let’s start with configuring the project’s pom dependencies.

First of all, we import the Holon Platform BOM (Bill Of Materials), this way we’ll no longer need to specify the Holon Platform artifacts version. The right version of each artifact is declared and provided by the platform BOM.

<dependencyManagement>
  <dependencies>
    <!-- Holon Platform BOM -->
    <dependency>
      <groupId>com.holon-platform</groupId>
      <artifactId>bom</artifactId>
      <version>5.0.2</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

We’ll use the Holon JAX-RS Spring Boot starter with Jersey as implementation, so we declare the following dependency:

<!-- Holon JAX-RS using Jersey -->
<dependency>
  <groupId>com.holon-platform.jaxrs</groupId>
  <artifactId>holon-starter-jersey</artifactId>
</dependency>

Do you prefer to use Resteasy as JAX-RS implementation? With Holon it’s only a matter of configuration, just change the Holon starter to use:

<!-- Holon JAX-RS using Resteasy -->
<dependency>
  <groupId>com.holon-platform.jaxrs</groupId>
  <artifactId>holon-starter-resteasy</artifactId>
</dependency>

These Holon Spring Boot starters inherit the standard Spring Boot web starters, and provide embedded servlet container configuration, logging and Holon specific auto-configuration facilities. By default, Tomcat is used as embedded servlet container. Want to use Undertow? Just change the starter name (for example, holon-starter-jersey-undertow). Concerning JSON data format instead, Jackson is automatically setted up. What if Gson is your preferred choice? It’s only about changing a name, again (for example, holon-starter-jersey-gson).

Now, independently from the starter you choosed, we have to create just 2 Java classes. The first one, which we call Application, is the Spring Boot application entry-point, which triggers auto-configuration and provides the main method used to run the application itself, starting the embedded servlet container and deploying our API endpoints:

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
  }
	
}

A application.yml file can be used to provide application configuration properties. For example, to configure the embedded server port:

server:
  port: 9999

Next it’s time to create the API endpoint, which will provide the API operations listening to the HTTP requests and returning the corresponding responses, using JSON as data format. As said before, this simple API will only provide a ping operation, mapped to the GET request method, returning a pong response encoded as JSON. Finally, we want to map the API operations endpoint to the base “/api” path. Using JAX-RS, this is how the API endpoint can be defined:

@Component
@Path("/api")
public class ApiEndpoint {

    @GET
    @Path("/ping")
    @Produces(MediaType.APPLICATION_JSON)
    public Response ping() {
        return Response.ok("pong").build();
    }

}

Note the @Component annotation on the API endpoint class. This is a standard Spring annotation to declare this class as a Spring component, candidate for auto-detection. Relying on the Holon Platform auto-configuration features, this class will be automatically detected and registered as a JAX-RS resource in Jersey (or Resteasy). And of course, will be enabled as a Spring component, allowing, for example, dependency injections.

To run the application, we’ll use the Spring Boot maven plugin, declaring it in the project pom:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <executions>
        <execution><goals><goal>repackage</goal></goals></execution>
      </executions>
    </plugin>
  </plugins>
</build>

This way, a runnable jar will be created by the Spring Boot plugin, with all the required dependencies and able to be execute standalone using a JRE.

To test the application, we can use the run Spring Boot maven goal this way:

mvn spring-boot:run

That’s it. The application can be now started, activating our API service. The ping operation will be available sending an HTTP GET request to an URL like this:

http://hostname:9999/api/ping

Obtaining a response like the following:

HTTP/1.1 200
Content-Type: application/json
Content-Length: 4
post

Documenting the API operations

We’ll use Swagger for API documentation. The Swagger specification is now the foudation of the Open API Initiative, which is trying to standardizing on how REST APIs are described.

The Holon Platform provides many configuration facilities for Swagger, perfectly integrated with JAX-RS and the Spring Boot auto-configuration architecture.

Let’s start adding the Holon Platform Swagger depencency to our project pom:

<dependency>		
  <groupId>com.holon-platform.jaxrs</groupId>
  <artifactId>holon-jaxrs-swagger</artifactId>
</dependency>

From now on, the Holon Platform auto-configuration services will auto-detect any JAX-RS endpoint annotated with the standard Swagger @Api annotation and automatically create a JAX-RS endpoint to generate and provide the Swagger API documentation. By default, this endpoint will be mapped to the /api-docs path and supports a type query parameter to obtain the Swagger API documentation as JSON (the default behaviour) or YAML.

Standard Swagger annotations are supported to enrich and configure the API documentation, such as @ApiOperation. Additionaly, the Holon Platform Swagger module provides the @ApiDefinition annotation, which can be used to configure the overall API informations (such the title and the version) and to change the default API documentation endpoint path. In this example, we want to use the /api/docs path to provide the API documentation.

So you want to modify the ApiEndpoint class this way:

@ApiDefinition(docsPath = "/api/docs", title = "Example API", version = "v1", prettyPrint = true)
@Api("Test API")
@Component
@Path("/api")
public class ApiEndpoint {

    @ApiOperation("Ping request")
    @ApiResponses({ @ApiResponse(code = 200, message = "OK: pong", response = String.class) })
    @GET
    @Path("/ping")
    @Produces(MediaType.APPLICATION_JSON)
    public Response ping() {
        return Response.ok("pong").build();
    }

}

This way, the Swagger API documentation can be obtained in JSON format from the URL:

http://hostname:9999/api/docs

Or in the YAML format:

http://hostname:9999/api/docs?type=yaml

Using the Swagger Editor to display the API documentation, it will appear like this:

Monitoring the API application

As a last step, we want to add application monitoring and management capabilities, using the Spring Boot Actuator, which adds several production grade services to obtain application information, health check, configuration and so on.

To enable the Spring Boot Actuator endpoints auto-configuration, we have to add the following dependencies to the project pom:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>4.3.11.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
  <version>1.5.8.RELEASE</version>
</dependency>

Now a set of monitoring endpoints should be registered. For example, the health check endpoint listens to the /health path. So we expect to obtain the health information performing a GET request to an URL like this:

http://localhost:9999/health

But wait… things do not go as we expected: the server renponds with a 404 (not found) error code! Why?

This is a well-known and annoying problem which occurs when the Jersey servlet is mapped to the root context path. The problem is that Jersey will get all the request. It does not know that it needs to forward to any actuator endpoints.

This can be resolved by change Jersey to be used as filter instead of a servlet. Using Spring Boot we can do this using a configuration property in the application.yml file:

spring:
  jersey:
    type: filter

But this is not enough: we also have to set the Jersey property to forward requests for URLs it doesn’t know. With the Holon Platform JAX-RS module this is simple, just another configuration property to set:

holon:
  jersey:
    forwardOn404: true

Well done, everything work as expected! The health check endpoint is reachable and provides the following JSON response content:

{"status":"UP"}

Summary

With 2 Java classes, a pom and a Spring Boot configuration file we setted up a production grade API application, able to be runned standalone, with API documentation and monitoring capabilities. Now it’s time to focus on business tasks, making the API somehow useful!

This this only the first article in a series that shows how to use the Holon Platform to face the API development challenges, including the microservices world, so stay tuned!


The source code of the example API created in this post is available on GitHub: https://github.com/holon-platform/spring-boot-jaxrs-swagger-api-example.

See the Holon Platform examples for other API development related sample projects.

We love the Vaadin framework and we use it from years now to build an amazing and modern User Experience for our internal projects and products. But above all, we choosed Vaadin to build the UI of many web applications for one reason: simplicity.

We share the values and objectives of the Vaadin fight for simplicity: focus on what matters the most, saving time which can be reemployed to create user value through better UX and features, and ensure freedom of choice, so that your choices are not limited by technologies, licensing or anything else.

The Holon Platform Vaadin module takes a step further towards semplicity and productivity, providing a fluent API to build the application components and backend connectors and a complete integration with the Holon Platform architecture, such as the Property model, the Datastore API, the authentication, authorization and localization support. Furthermore, Spring and Spring Boot are supported out-of-the-box, to exploit Spring’s great feautures and services and forget about the most annoying configuration concerns.

Check the full-stack web application tutorial to learn how to build a simple web application in minutes with Vaadin and the Holon Platform modules, using the Java language only.

The fight for simplicity is just started, and we want to be part of it. Join us and get started with the Holon Platform!

One of the Holon platform foundation concepts is the Property model, which can be used to represent a generic data model, abstracting away from data structure and persistence implementation details. The property model architecture is designed for maximum flexibility and versatility, so as not to limit its use to backend or data management application levels, but to make it a cross-cutting asset, to be seamlessy used in a multitude of situations and application or communication layers.

The Property interface, as PathProperty or VirtualProperty implementation in particular, along with the PropertySet and PropertyBox structures, allow you to obtain the main following benefits:

  • Collect all the relevant features and configuration data in a single point, represented by a Property, to avoid duplications and inconsistency between application layers;
  • Abstract away the property definition from the concrete data representation and persistence model, to favor loose coupling and independence from underlying data structures, extensibility and versatibility;
  • Provide common operations and functionalities, such as value converters and validators, in a consistent and well defined way;
  • Enable the use of a standard API to manage the data model, relying on the Property architecture abstractions, for example to query the data model or to transport data in the form of property values.

Read the reference documentation to get all the information about the Property model and to explore the Holon Platform APIs which allow you to define and use it to the maximum of its potential.

Or just start coding, following the property model tutorial and exploring the examples available on GitHub.

We use cookies to ensure that we give you the best experience on our website.