Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/DesignDocs/MultiTenantFeatures"

(Mapped Attributes for Extension)
 
(11 intermediate revisions by one other user not shown)
Line 1: Line 1:
'''UNDER CONSTRUCTION'''
+
<div style="float:right;width:300px">
 +
__TOC__
 +
</div>
  
 
== Purpose ==
 
== Purpose ==
  
EclipseLink provides a number of features that help with the challenges of developing multi tenant applications.  This document will provide an overview of some of the ways EclipseLink can be used to support multi tenant applications and suggest some enhancements that could be made to further enable thos applications.
+
EclipseLink provides a number of features that help with the challenges of developing multi-tenant applications.  This document will provide an overview of some of the ways EclipseLink can be used to support multi-tenant applications and suggest some enhancements that could be made to further enable those applications.
  
 
Some of the challenges involved in developing multitenant applications include:
 
Some of the challenges involved in developing multitenant applications include:
  
# You must choose if many tenants reside on the same DB, or the same schema
+
# You must decide whether many tenants share a database or tablespace
 
# Will the schema be flexible?  If so, hom much?
 
# Will the schema be flexible?  If so, hom much?
# How do I differentiate data for tenants that share the same DB?
+
# How do I differentiate data for tenants that share the same DB or tablespace?
  
EclipseLink provides features that support various decision patchs about the above issues.
+
EclipseLink provides features that support various decision paths about the above issues.
  
 
Here are some examples of the types of multi-tenant applications and how you could enable them in Eclipselink.
 
Here are some examples of the types of multi-tenant applications and how you could enable them in Eclipselink.
  
== Static Application ==
+
== Application Architectures ==
 +
 
 +
=== Static Application ===
  
 
In this application, neither the object model nor the database schema is variable.  Tenants, for the most part, use the application as it has been provided.  If they want to make use of data that is not part of the initial application they are provided a limited number of predefined database fields and cooresponding fields in the object model.
 
In this application, neither the object model nor the database schema is variable.  Tenants, for the most part, use the application as it has been provided.  If they want to make use of data that is not part of the initial application they are provided a limited number of predefined database fields and cooresponding fields in the object model.
  
 
For Example:
 
For Example:
 +
<pre>
 +
Table Customer contains fields: id, name, attribute1, attribute2
 +
Class Customer contains fields: id, name, attribute1, attribute2
 +
</pre>
  
''Table Customer contains fields: id, name, attribute1, attribute2''
+
In the above example, the id and name fields have predefined functionality in the application.  Fields attribute1, and attribute2 can be configured to store any data the customer wants.  Typically these fields store data that is mapped direct from Object to database. (In JPA, a Basic mapping)
''Class Customer contains fields: id, name, attribute1, attribute2''
+
  
In the above example, the id and name fields have predefined functionality in the application.  Fields attribute1, and attribute2 can be configured to sore any data the customer wants.  Typically these fields store data that is mapped direct from Object to database. (In JPA, a Basic mapping)
+
This application resembles a basic JPA application and, has at its disposal, all the functionality available in JPA and the extended functionality available in EclipseLink.  Basically, the application acquires an EntityManagerFactory for each tenant and that EntityManagerFactory serves that tenant only.
 
+
This application resembles a basic JPA application and has at its disposal, all the functionality available in JPA and the extended functionality available in EclipseLink.  Basically, the application acquires an EntityManagerFactory for each tenant and that EntityManagerFactory serves that tenant only.
+
  
 
Some features of interest that exist in EclipseLink:
 
Some features of interest that exist in EclipseLink:
  
# Converters - If the attribute1 and attribute2 fields need to be stored as different types in the database and in the application, converters could be used to address this
+
* Converters - If the attribute1 and attribute2 fields need to be stored as different types in the database and in the application, converters could be used to address this
# VPD Support - When using an Oracle Database, VPD provides an excellent way of securly segregating data between clients.  (i.e. with VPD, it is easy to make the same table contain data for different tenants have automatically have only data for a particular tenant used in queries).  VPD users can be chosen through EclipseLink's proxy authentication feature and specified through properties in creation of EntityManager factories.
+
* VPD Support - When using an Oracle Database, VPD provides an excellent way of securly segregating data between clients.  (i.e. with VPD, it is easy to make the same table contain data for different tenants have automatically have only data for a particular tenant used in queries).  VPD users can be chosen through EclipseLink's proxy authentication feature and specified through properties in creation of EntityManager factories.
# Additional Criteria Support - If VPD is not available, it is possible to segregate data by setting an Additional Criteria for a descriptor.  This criteria could be used to ensure only data appropriate to a specific client would be returned.
+
* Additional Criteria Support - If VPD is not available, it is possible to segregate data by setting an Additional Criteria for a descriptor.  This criteria could be used to ensure only data appropriate to a specific client would be returned.
# SessionCustomizer/DescriptorCustomizer - these could be used in the configuration of persistence units for each tenant. The Additional Criteria listed above is an example of what could be specified
+
* SessionCustomizer/DescriptorCustomizer - these could be used in the configuration of persistence units for each tenant. The Additional Criteria listed above is an example of what could be specified
# Dynamic Specification of persistence.xml - EclipseLink will allow the persistence.xml for a persistence unit to be specified at runtime.  This is an additional way to support different configurations per tenant.  In this case, each tenant would have a persistence.xml that configured the persistence unit for their part of the application.
+
* Dynamic Specification of persistence.xml - EclipseLink will allow the persistence.xml for a persistence unit to be specified at runtime.  This is an additional way to support different configurations per tenant.  In this case, each tenant would have a persistence.xml that configured the persistence unit for their part of the application.
  
 
Some features that could be implemented to help:
 
Some features that could be implemented to help:
  
# VPD Equivalents for databases other that Oracle -  Other vendors provide similar functionality and this could be support if there was demand.
+
* VPD Equivalents for databases other that Oracle -  Other vendors provide similar functionality and this could be supported if there was demand.
# Dynamic specification of mapping xml.  If mapping xml file could be specified at EntityManagerFactory creation time that would reduce the need to specify different persistence.xml files and any mapping/descriptor overrides could be specified in that file.
+
* Dynamic specification of mapping xml.  If mapping xml file could be specified at EntityManagerFactory creation time that would reduce the need to specify different persistence.xml files and any mapping/descriptor overrides could be specified in that file.
  
=== Mapped Attributes for Extension ===
+
=== Extended Attributes use Map ===
  
 
To provide slightly more flexibility than the Static Application, a Map could be used for extended attributes.
 
To provide slightly more flexibility than the Static Application, a Map could be used for extended attributes.
Line 48: Line 53:
  
 
<pre>
 
<pre>
Table Customer contains fields: id, name''
+
Table Customer contains fields: id, name
Table Customer_extensions contains fields: customer_id, attributeName, attributeValue''
+
Table Customer_extensions contains fields: customer_id, attributeName, attributeValue
 
Class Customer contains fields: id, name, extensions
 
Class Customer contains fields: id, name, extensions
 
</pre>
 
</pre>
Line 57: Line 62:
 
Some features of interest that exist in EclipseLink:
 
Some features of interest that exist in EclipseLink:
  
# Map Mapping Strategies: EclipseLink Supports map mapping strategies with many different types of keys and values.
+
* Map Mapping Strategies: EclipseLink Supports map mapping strategies with many different types of keys and values.
  
 +
=== Metadata Driven ===
  
 +
It is possible to write a completely metadata driven application in EclipseLink using our Dynamic JPA feature set.  A mapping xml file is sufficient to define the Object model.  The idea here is that the application provider would define the metadata for the core of the application in XML and provide a mechanism where the tenant could define additional/changed metadata through an additional XML file.
  
 +
The level of flexibility here is completely dependant on the level of functionality of the database schema.
  
 +
# For a schema like the one listed in our "Static Application" section, the extensibility would be limited to naming the extended part of the object model correctly (e.g. attribute1 in the Customer object could be called "EmployeeID instead)
 +
# For a scenario where each tenant had their own database or schema,  DDL could be generated to add to the default database based on the user's metadata changes
 +
# "Flex Tables" could be provided in a similar way to the flexible columns listed in the "Static Application" scenario.  (i.e. Tables with generic names and a predefined set of columns) These tables could be used to house dynamically generated entities, or as secondary tables to hold additional attributes of existing entities.
  
 +
Some features of interest that exist in EclipseLink:
  
 +
* Dynamic JPA - Allows full object model to be defined in xml or in code based on metadata
 +
* Secondary tables support - Descriptors can use secondary tables to house additional data.  Base table could hold application-developer-defined information.  Secondary table could hold tenant defined information.
 +
* DDL Generation - Could be used to define tables for tenant applications
  
As part of the data-access layer of an application, EclipseLink features that enable multi-tenant application fall into one of two categories.
+
Some features that could be implemented to help:
  
# Features that enable data-models for multi-tenant applications
+
* Schema Alteration - allow alter table statements to be issued based on metadata changes by tenant
# Features that enable object models for multi-tenant applications
+
* Selective DDL generation - allow DDL generation of certain tables to be switched off
  
 +
=== Partially Metadata Driven ===
  
EclipseLink provides a number of features that help with the challenges of developing multi tenant applicationsThis document will provide an overview of some of the ways EclipseLink can be used to support multi tenant applications.
+
With some small changes to EclispeLink, a hybrid of a Static Application and a Metadata driven application could be developedIn this case, the application developer would define static classes, but allow them to be extended by our dynamic JPA feature set.
  
In particular, this document will focus on applications where the application provider provides a core application and the tenant needs to extend the data provided in the application. (like Oracle Applications flex columns)
+
For example, the application developer would develop an application that contained a Customer class that contains an id and name field but also allow it to be extended by the tenant by allowing an xml file defining extended mappings to be developed.
  
e.g. The application provider provides an Employee class that includes name and address.  The tenant also wants to store the Employee Number.  How can the application be architected to make storing that kind of extra data easy.
+
This kind of application would depend on the same database schema restrictions as the metadata driven application, and allow tenants running on the basic functionality to use only the static classes.
  
From a persistence-layer point of view, there are two areas where you must design to allow extra data.
+
Some features that could be implemented to help:
  
# The object model and metadata
+
* Hybrid Static/Dynamic class support - allow static classes to subclass our dynamic type and be extended with dynamic attributes.  Partial prototype see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=331915
# The database
+
  
We will outline the options for both areas.
+
== Application configuration ==
  
== Object Model and Metadata ==
+
There are many different options for configuration of the persitence units for use in multi-tenant applications.
  
There are a number of different ways EclipseLink can support added fields in the object model.
+
=== Single Persistence Unit ===
  
=== Predefined fields ===
+
A single persistence unit could be provided that maps all the attributes.  This strategy is useful in the cases where both the schema and the object model remain the same.
  
In this case, a class is designed with several generically named and typed fields.  Those fields may be used by the tenant to store any data they choose.  Converters could be used to control the data types coming out of the fields.
+
=== Deployment per tenant ===
  
e.g. Employee contains fields called name, address, attribute1, attribute2 etc...  The tenant chooses to store employee number in attribute1.
+
A full persistence unit could be configured per tenant. That persistence unit could contain different persistence.xml, mapping files, classes and resources. This could be used with any of the above strategies. This is on the opposite extreme from a Single Persistence unit deploymentIt allows complete configuration for each tenant, of both the data and object model and could be used with any of the application designs listed above.
  
=== Dynamic JPA ===
+
=== Persistence xml per tenant ===
  
Dynamic JPA allows the construction of a completely metadata driven application.  The provider writes a dynamic JPA application that defines the set of classes and attributes that existThe tenant calls API or provides XML that describes the additional fields on those classes.
+
In this case, each tenant has a persistence.xml file which creates them a separate persistence unit.  The classes used by each tenant remain the same.  EclipseLink supports dynamic selection of persistence.xml so as long as the persistence xml for each tenant could be provided, this could all be configured in one deploymentThis option allows different session-level configuration options for each tenant and could be used as a mechanism for replacing mapping information as well.  This type of a configuration is a candidate for any of the application designs indicated above.
  
e.g. Employee is a dynamically created entity containing name and address attributes.  The tenant is allowed to construct a mapping for Employee Number by constructing an EclipseLink-orm.xml file that defines Employee with the new attribute.
+
=== eclipselink-orm.xml per tenant ===
  
=== Static Classes + Dynamic JPA ===
+
''This requires EclipseLink to be enhanced to allow dynamic specification of orm xml''
  
A static object model is provided by the provider that defines all the shared attributes. The tenant is provided with mechanism to provide extensions through Dynamic JPA.  They are allowed to construct new mappings on the static classes by providing XML that describes fields that will be added to the existing object model through dynamic JPA.
+
It is possible to add a persistence unit property that allows orm.xml files to be provided by on a per-tenant basis.  In this case, everything about the persistence unit would be the same except a single eclipselink-orm.xml file which defines the mappings that are particular to a single tenant.  This type of a configuration is a candidate for any of the application designs indicated above.
  
e.g. Employee is a class that contains name and address attributes.  The tenant is allowed to construct an eclipselink-orm.xml file that adds a virtual Employee Number field to Employee.  EclipseLink's dynamic JPA functionality handles adding the field.
+
=== Metadata in the db ===
  
See https://bugs.eclipse.org/bugs/show_bug.cgi?id=331915 for the remaining work required for this configuration.
+
''This requires a feature that allows metadata to be stored in the database.  This feature would likely have to be implemented both within EclipseLink and within the application server it runs on.''
 
+
=== Map ===
+
 
+
Additional attributes are specified in a Map Mapping.  Business logic is used to expose the fields in the Map as standard data.
+
 
+
e.g. Employee has attributes name and address and also a map for additional properties.  To add Employee number, the tenant adds a Map Entry with key=EmployeeNumber and Value=<employees Employee Number>
+
 
+
== Database ==
+
 
+
There are many different database architectures that can be used to support storing extra data.
+
 
+
 
+
=== Different Schema ===
+
 
+
If the tenants are all deployed on different databases or on different table spaces, the tables can quite simply be different.  This can be enabled by EclipseLink's table creation feature.  Additionally, it would be possible to introduce an EclipseLink feature that could alter tables in an initial schema based on different metadata.
+
 
+
e.g. A base tenant would have an Employee table with just a name and address field.  Another tenant could have an Employee table with a name, address, and employee number field.
+
 
+
=== Static Fields ===
+
 
+
Simply define extra fields in each database table that can used to store extra data.
+
 
+
e.g. A table for Employee would contain name, address, attribute1, attribute2 etc...
+
 
+
This table architecture can be used with most of the object model strategies above.
+
 
+
=== Secondary Tables ===
+
 
+
EclipseLink supports secondary tables and these could be used to extend object models.
+
 
+
e.g. A base Employee table could contain name and address, a secondary table could contain a foreign key to the base table, and a set of additional field. (e.g. employee number, or attribute1 etc)
+
 
+
Secondary tables could be provided with preset fields in much the same way as the Static Field model above, or secondary tables could be created for each tenant that needed extra data.  Dynamic metadata could be provided to change each tenants mapping to access their tables.
+
 
+
=== Map Table ===
+
 
+
Attributes are stored in a table that contains a key, an attribute name, and an attribute value.  The table could optionally have a foreign key that mapped to a source table.
+
 
+
e.g. Employee table contains an id (and possibly some of the data).  Attributes are contained in a table that contains a foreign key to the Employee table, an attribute name and an attribute value.
+
 
+
With this strategy, the map table could contain all the attributes, or the main table could contain some of the attributes and the map table could contain the rest.
+
 
+
== Differentiating between tenants ==
+
 
+
EclipseLink provide 2 features that could be used to differentiate between tenants on the database
+
 
+
# Support for Oracle VPD - Oracle users can use Oracle's VPD feature through EclipseLink and that feature can be used to differentiate between tenants.
+
# Additional Join Criteria - EclipseLink allows additional join criteria to be specified on each descriptor that could be used for tenant differentiation
+
 
+
Other databases provide VPD-like features and support could be added for those as they are required.
+
 
+
== EclipseLink Config ==
+
 
+
=== Configuration Strategies ===
+
 
+
The following configuration options can be used to configure the different stratgies
+
 
+
==== Single Persistence Unit ====
+
 
+
A single persistence unit could be provided that maps all the attributes.  This strategy is useful in the case where there is no dynamic component to the application, all the db fields and all the attributes remain the same.
+
 
+
==== Deployment per tenant====
+
 
+
A full persistence unit could be configured per tenant.  That persistence unit could contain different persistence.xml, mapping files, classes and resources.  This could be used with any of the above strategies.
+
 
+
==== Persistence xml per tenant ====
+
 
+
In this case, each tenant has a persistence.xml file which creates them a separate persistence unit.  The classes used by each tenant remain the same.  EclipseLink supports dynamic selection of persistence.xml so as long as the persistence xml for each tenant could be provided, this could all be configured in one deployment.
+
 
+
==== eclipselink-orm.xml per tenant ====
+
 
+
It is possible to add a persistence unit property that allows orm.xml files to be provided by on a per-tenant basis. In this case, everything about the persistence unit would be the same except a single eclipselink-orm.xml file which defines the mappings that are particular to a single tenant.
+
 
+
==== Customizer per tenant ====
+
 
+
A customizer could be used to configure tenant data.  Each tenant would have a customizer that cusomized their persistence unit.
+
 
+
=== Metadata in the db ===
+
  
Ideally if there are multiple config files they should be stored in the DB along with other tenant informationWe will have to add extensions to allow config files to be retrieved from the DB.
+
An easy way to store multiple configurations is in some kind of a database(either a relational db, or a SCM system)  Ideally, configuration information could be stored in that way and EclipseLink could be configured to find that information.

Latest revision as of 14:22, 23 February 2011

Purpose

EclipseLink provides a number of features that help with the challenges of developing multi-tenant applications. This document will provide an overview of some of the ways EclipseLink can be used to support multi-tenant applications and suggest some enhancements that could be made to further enable those applications.

Some of the challenges involved in developing multitenant applications include:

  1. You must decide whether many tenants share a database or tablespace
  2. Will the schema be flexible? If so, hom much?
  3. How do I differentiate data for tenants that share the same DB or tablespace?

EclipseLink provides features that support various decision paths about the above issues.

Here are some examples of the types of multi-tenant applications and how you could enable them in Eclipselink.

Application Architectures

Static Application

In this application, neither the object model nor the database schema is variable. Tenants, for the most part, use the application as it has been provided. If they want to make use of data that is not part of the initial application they are provided a limited number of predefined database fields and cooresponding fields in the object model.

For Example:

Table Customer contains fields: id, name, attribute1, attribute2
Class Customer contains fields: id, name, attribute1, attribute2

In the above example, the id and name fields have predefined functionality in the application. Fields attribute1, and attribute2 can be configured to store any data the customer wants. Typically these fields store data that is mapped direct from Object to database. (In JPA, a Basic mapping)

This application resembles a basic JPA application and, has at its disposal, all the functionality available in JPA and the extended functionality available in EclipseLink. Basically, the application acquires an EntityManagerFactory for each tenant and that EntityManagerFactory serves that tenant only.

Some features of interest that exist in EclipseLink:

  • Converters - If the attribute1 and attribute2 fields need to be stored as different types in the database and in the application, converters could be used to address this
  • VPD Support - When using an Oracle Database, VPD provides an excellent way of securly segregating data between clients. (i.e. with VPD, it is easy to make the same table contain data for different tenants have automatically have only data for a particular tenant used in queries). VPD users can be chosen through EclipseLink's proxy authentication feature and specified through properties in creation of EntityManager factories.
  • Additional Criteria Support - If VPD is not available, it is possible to segregate data by setting an Additional Criteria for a descriptor. This criteria could be used to ensure only data appropriate to a specific client would be returned.
  • SessionCustomizer/DescriptorCustomizer - these could be used in the configuration of persistence units for each tenant. The Additional Criteria listed above is an example of what could be specified
  • Dynamic Specification of persistence.xml - EclipseLink will allow the persistence.xml for a persistence unit to be specified at runtime. This is an additional way to support different configurations per tenant. In this case, each tenant would have a persistence.xml that configured the persistence unit for their part of the application.

Some features that could be implemented to help:

  • VPD Equivalents for databases other that Oracle - Other vendors provide similar functionality and this could be supported if there was demand.
  • Dynamic specification of mapping xml. If mapping xml file could be specified at EntityManagerFactory creation time that would reduce the need to specify different persistence.xml files and any mapping/descriptor overrides could be specified in that file.

Extended Attributes use Map

To provide slightly more flexibility than the Static Application, a Map could be used for extended attributes.

In this case a model could be designed like this:

Table Customer contains fields: id, name
Table Customer_extensions contains fields: customer_id, attributeName, attributeValue
Class Customer contains fields: id, name, extensions

extensions would be defined using a Map mapping and hold any additional fields the customer wanted to use. For instance, an Employee Number would be stored with key = "EmployeeNumber" and the value = <employee number>. Business logic is used to expose the data in extensions as attributes.

Some features of interest that exist in EclipseLink:

  • Map Mapping Strategies: EclipseLink Supports map mapping strategies with many different types of keys and values.

Metadata Driven

It is possible to write a completely metadata driven application in EclipseLink using our Dynamic JPA feature set. A mapping xml file is sufficient to define the Object model. The idea here is that the application provider would define the metadata for the core of the application in XML and provide a mechanism where the tenant could define additional/changed metadata through an additional XML file.

The level of flexibility here is completely dependant on the level of functionality of the database schema.

  1. For a schema like the one listed in our "Static Application" section, the extensibility would be limited to naming the extended part of the object model correctly (e.g. attribute1 in the Customer object could be called "EmployeeID instead)
  2. For a scenario where each tenant had their own database or schema, DDL could be generated to add to the default database based on the user's metadata changes
  3. "Flex Tables" could be provided in a similar way to the flexible columns listed in the "Static Application" scenario. (i.e. Tables with generic names and a predefined set of columns) These tables could be used to house dynamically generated entities, or as secondary tables to hold additional attributes of existing entities.

Some features of interest that exist in EclipseLink:

  • Dynamic JPA - Allows full object model to be defined in xml or in code based on metadata
  • Secondary tables support - Descriptors can use secondary tables to house additional data. Base table could hold application-developer-defined information. Secondary table could hold tenant defined information.
  • DDL Generation - Could be used to define tables for tenant applications

Some features that could be implemented to help:

  • Schema Alteration - allow alter table statements to be issued based on metadata changes by tenant
  • Selective DDL generation - allow DDL generation of certain tables to be switched off

Partially Metadata Driven

With some small changes to EclispeLink, a hybrid of a Static Application and a Metadata driven application could be developed. In this case, the application developer would define static classes, but allow them to be extended by our dynamic JPA feature set.

For example, the application developer would develop an application that contained a Customer class that contains an id and name field but also allow it to be extended by the tenant by allowing an xml file defining extended mappings to be developed.

This kind of application would depend on the same database schema restrictions as the metadata driven application, and allow tenants running on the basic functionality to use only the static classes.

Some features that could be implemented to help:

Application configuration

There are many different options for configuration of the persitence units for use in multi-tenant applications.

Single Persistence Unit

A single persistence unit could be provided that maps all the attributes. This strategy is useful in the cases where both the schema and the object model remain the same.

Deployment per tenant

A full persistence unit could be configured per tenant. That persistence unit could contain different persistence.xml, mapping files, classes and resources. This could be used with any of the above strategies. This is on the opposite extreme from a Single Persistence unit deployment. It allows complete configuration for each tenant, of both the data and object model and could be used with any of the application designs listed above.

Persistence xml per tenant

In this case, each tenant has a persistence.xml file which creates them a separate persistence unit. The classes used by each tenant remain the same. EclipseLink supports dynamic selection of persistence.xml so as long as the persistence xml for each tenant could be provided, this could all be configured in one deployment. This option allows different session-level configuration options for each tenant and could be used as a mechanism for replacing mapping information as well. This type of a configuration is a candidate for any of the application designs indicated above.

eclipselink-orm.xml per tenant

This requires EclipseLink to be enhanced to allow dynamic specification of orm xml

It is possible to add a persistence unit property that allows orm.xml files to be provided by on a per-tenant basis. In this case, everything about the persistence unit would be the same except a single eclipselink-orm.xml file which defines the mappings that are particular to a single tenant. This type of a configuration is a candidate for any of the application designs indicated above.

Metadata in the db

This requires a feature that allows metadata to be stored in the database. This feature would likely have to be implemented both within EclipseLink and within the application server it runs on.

An easy way to store multiple configurations is in some kind of a database. (either a relational db, or a SCM system) Ideally, configuration information could be stored in that way and EclipseLink could be configured to find that information.

Back to the top