Developing Multitenant Applications with JRapid

From JRapid

Jump to: navigation, search

Contents

Description

Multitenancy refers to a principle in software architecture where a single instance of the software runs on a server, serving multiple client organizations (tenants). In a Multitenant environment, multiple customers share the same application, running on the same operating system, on the same hardware, with the same data storage mechanism. The distinction between the customers is achieved during application design, so that customers do not share or see each other's data.

File:MultiTenant.png‎

There are several ways to organize the data and distinguish between customers in multitenant applications. They include isolating data (separate databases) and sharing data (same database, same schema, same tables). A third way to separate the data is having different schema and tables for different tenants.

Separate DB

Giving each tenant its own database makes it easy to extend the application's data model to meet individual needs and restoring tenant's data from backups. Unfortunately, this approach tends to lead to higher costs for maintaining equipment and backing up tenant data. Hardware costs are also higher than they are under alternative approaches, as the number of tenants that can be housed on a given database server is limited by the number of databases that the server can support. Separating tenant data into individual databases is the "premium" approach, and the relatively high hardware and maintenance requirements and costs make it appropriate for customers that are willing to pay extra for added security and customization.

Shared Schema

On the other extreme, tenants may share the same database schema and by extension share the same tables. For each database table a tenantID column differentiates tenant's data. This approach has the lowest hardware and backup costs, because it allows you to serve the largest number of tenants per database server. However, because multiple tenants share the same database tables, this approach may incur additional development effort in the area of security, to ensure that tenants can never access other tenants' data, even in the event of unexpected bugs or attacks. Also, backing up a particular tenants data would be more complicated than if they each have their own database.

Separate Schema

Another approach involves housing multiple tenants in the same database, with each tenant having its own set of tables that are grouped into a schema created specifically for the tenant. Tenants can extend the data model as easily as with the separate-database approach. (Tables are created from a standard default set, but once they are created they no longer need to conform to the default set, and tenants may add or modify columns and even tables as desired.) This approach offers a moderate degree of logical data isolation for security-conscious tenants, though not as much as a completely isolated system would, and can support a larger number of tenants per database server.

A significant drawback of the separate-schema approach is that tenant data is harder to restore in the event of a failure. If each tenant has its own database, restoring a single tenant's data means simply restoring the database from the most recent backup. With a separate-schema application, restoring the entire database would mean overwriting the data of every tenant on the same database with backup data, regardless of whether each one has experienced any loss or not. Therefore, to restore a single customer's data, the database administrator may have to restore the database to a temporary server, and then import the customer's tables into the production server—a complicated and potentially time-consuming task.

Designing multitenant applications in JRapid

Fortunately, the process of turning a single-tenant application into a multitenant application in JRapid is a short effort and few minute process if you decide to use the separate database or separate scheman approaches. JRapid applications can be easily designed to support both isolated approaches at the same time. This will be explained in detail later. If you chose to implement the shared approach then you will have to redesign your entities by adding the corresponding tenant properties.

As said above, the process of enabling multitenancy is a short effort process. Actually, you will have to code or copy/paste a few simple lines to enable multitenancy but in the future the web IDE will be prepared to do it for you. Before explaining how to design your application to enable multitenancy I will first discuss how tenants are registered in the application. This will help to understand the changes that we will have to make to the application.

Example

The following example will walk you through setting up a second database in your jrapid.properties file, and then implementing the code so that your application can differentiate schemas and direct your client correctly. The project you are adding this to should include the imported Login, User and Role entities from the User management template, as these users will guide how the client logs in and what access they have.

Adding a database schema and new tenant

When you have a new client looking to use your JRapid application it is easy to add them. If you decided to use a multitenant architecture you will not deploy your project in another database, instead you will register a new tenant in the "super" application. Registering tenants consists of the same process either for tenants in different databases and tenants in different schema in the same database.

To register a new tenant open the jrapid.properties file. For a given "tenantname" tenant you will have to add a tenantname.connection.url, tenantname.connection.username, tenantname.connection.password and tenantname.repository properties to the file.

   * url: the JDBC URL for the database.
   * username: database username.
   * password: database password.
   * repository: path to the repository for storing tenant's own files like reports, images, etc


File:dbImage.jpg‎

In the above image we have two tenants registered in the application, "default" (which is generally used for development) and "test". These tenants are hosted in the same database"db1.jrapid.com" but this is not a requirement. The only restriction in the URL property is that all tenants should use the same database engine. This means that you can't register a tenant which host its data using a MySQL database and other which uses an Oracle database.

Configuring your project

Now that the schema is added we want to configure the project so that the client is validated and has access. This is done by redirecting them to the login screen and then using that information to determine the schema they will use. Each tenant will access the application using an unique URL. The difference between tenants URLs will reside in the schema parameter.

Example:

  1. http://demo.jrapid.com/libraryproject/?schema=default
  2. http://demo.jrapid.com/libraryproject/?schema=test

To do this, you should edit the index.jsp file which is the default welcome file (see the web.xml file). This JSP file should set the specified schema in the local session and redirect the user to a login page which is normally Login_form.html and placed in the auto-created Main directory in your WebContent folder. Once the schema is specified in the Session, the user will only see stored information for that schema and no other.

  <%@page import="com.jrapid.controller.Session" %>
  <% Session.getMySession().setSchema(request.getParameter("schema")); %>
  <% response.sendRedirect("Main/Login_form.html"); %>

Do not forget to edit the "next" element of the Login entity which specifies the next JavaScript function to be executed after a successful login. By default the "next" function is window.location.reload() but you will want to redirect the user to a Panel or some other HTML page. In the below example, the user is redirected to the company form after a successful login.

<entity encrypt="encrypt" label="Log In" name="Login" stereotype="Login" transient="transient">
  <row name="data">
    <column>
      <property display="primary" label="Username" name="username" required="required"/>
      <property label="Password" name="password" required="required" type="password"/>
      <property default="'default'" label="Schema" name="schema" required="required" type="enum"
      hidden="hidden"/>
    </column>
  </row>
  <next function="window.location.reload();" type="function"/>
</entity>

The next function is necessary to reload the page after a successful login and so that the user gets redirected to their initial destination, index.html. Next can also be linked to an external JavaScript file either Main.js which is automatically created and included for you in WebContent/Main/js, or in one you define yourself and link with usescript in your configuration section of the source.

You should now see a login upon attempting to access the application. If you create two logins with different schemas, then you should be able to test this out by logging in as one user, creating one or more entities, and then logging in with the other login. The other login should then not be able to see the data already created.

Personal tools