Skip to content

Customizing Generated Services

You can customize the generated service to include business logic.

Customizable Files

Depending on the options you selected for the service generator tool, generated files may include the following files which can be customized. Regeneration results in necessary changes being automatically merged into these files.

  • pom.xml (Maven Project Object Model) – you must edit this file if your customized Java code uses third-party libraries that need to be listed as Maven dependencies.

  • manifest.yml (Cloud Foundry deployment manifest).

  • resources.xml (TomEE JDBC resource configuration).

Generated Java source files include the following classes that can be customized. Regeneration results in no changes to these files. All the Java files in the top-level folder are user-customizable.

  • Log settings class.

  • Metric settings class.

  • Provider settings class.

  • Test settings class.

  • Listener classes (in the listener subfolder) for customization of OData CRUD operations.

  • Handler classes (in the handler subfolder) for implementation of OData CRUD operations and actions and functions.

  • Main servlet class (main server entry point for dispatching client's OData requests). MainServlet (in top-level folder) inherits from MainServletBase (in base subfolder).

  • Metric servlet class (an embedded OData service that provides administrators with usage metrics for the main service). MetricServlet (in top-level folder) inherits from MetricServletBase (in base subfolder).

Generated Java source files include the following classes that you should not customize as they will be discarded and regenerated if you run the service generator again.

  • Classes in the generated base subfolder.

  • OData proxy classes (in the proxy subfolder). These classes are used by customization logic that requires access to the database.

After you've made any customization changes, rebuild the WAR file, e.g. via Terminal > Run Task... in Visual Studio Code or SAP Business Application Studio.

Listener Classes

Customizing generated listener classes lets you add code blocks that are executed before or after OData CRUD Operations.

The additional code blocks can use the service object to send additional queries or updates to the database, or simply perform data cleansing or data validation.

The following is an example of data cleansing:

@Override void beforeSave(EntityValue entityValue)
{
    // Remove leading and trailing spaces from customer name before saving changes to database.
    Customer entity = (Customer)entityValue;
    String name = entity.getName();
    entity.setName(name.trim());
}

The following is an example of data validation:

@Override void beforeSave(EntityValue entityValue)
{
    // Check that order item quantity is positive.
    OrderItem entity = (OrderItem)entityValue;
    int quantity = entity.getQuantity();
    if (quantity <= 0)
    {
        throw DataServiceException.validationError("Order item quantity must be a positive number.");
    }
}

You can also use listener classes to implement auditing and fine-grained security; see Row-Level Authorization and Column-Level Authorization in Securing the Generated Service.

Handler Classes

Customizing generated handler classes lets you override the default implementation of OData CRUD Operations as well as actions and functions.

You'll probably not need to override the implementation of OData CRUD Operations: sections in handler classes are usually sufficient. A special case is, for example, overriding of executeQuery so that the query result from the database is post-processed, to remove rows and columns containing sensitive data before it's returned to the client. This might be appropriate if you were unable to achieve the post-processing by modifying the query filter before execution using a beforeQuery listener.

OData actions and functions must be implemented by your customization code, as it's usually impossible for the code generator to know what an action or function should do. If you don't explicitly an action or function by customizing the respective method handler class, the client receives an HTTP 501 (Not Implemented) error.

Server OData API

Customization code frequently uses classes from the Server OData API (see Javadoc).

To access the incoming request, the generated listener and handler classes provide access to the servlet object; see the DataServlet class.

To access the back-end database, the generated listener and handler classes provide access to the service object; see the DataService class.

To create or alter database queries, see the DataQuery class.

You can use the generated OData proxy classes for type-safe access to OData entity types. Note that the service object also provides convenient type-safe access to query results, by the generation of a strongly typed query method for each OData entity set.

Adding Seed Data

Provisioning initial data for entity sets is optional.

When a database table is created, initial data is automatically loaded from the web application archive (WAR file) and inserted into the database.

In the generated Maven project (in source code), the resource subfolder for initial data is src/main/resources/initial-data. For a compiled Java servlet-based application, the resource subfolder for initial data is WEB-INF/classes/initial-data.

Each initial data file uses the name EntitySetName.json. For example, create the initial data for a OrderSet entity in the subfolder src/main/resources/initial-data and call the file OrderSet.json. Once the project is compiled and is packaged into a Java EE WAR file using Maven, that file is located in WEB-INF/classes/initial-data/OrderSet.json within the WAR file.

Example:

[
    {
        "OperationID" : 1,
        "ServiceOrderID" : 1,
        "critical" : true,
        "description": "Clean exterior filter"
    },
    {
        "OperationID" : 2,
        "ServiceOrderID" : 1,
        "critical" : true,
        "description": "Check freon pressure"
    },
    {
        "OperationID" : 3,
        "ServiceOrderID" : 2,
        "critical" : false,
        "description": "Tighten fan belt"
    },
    {
        "OperationID" : 4,
        "ServiceOrderID" : 3,
        "critical" : false,
        "description": "Use blower to clean ducts"
    },
    {
        "OperationID" : 5,
        "ServiceOrderID" : 3,
        "critical" : false,
        "description": "Clean exterior grills"
    }
]

Note

  • Key values in initial data are usually not reflected in the target database; they serve only to indicate relationships between entities in separate JSON files.
  • Initial data is only inserted into the database if it is available in the WAR file when the server application is first started. Subsequently adding initial data to your project and redeploying your OData service (without a version number change) will result in the initial data not being inserted into the database.

Nesting Child Entities

The initial data files contain one entity set per file. We recommend that you use nested child entity sets that reflect the relationship between the data.

Example:

[
    {
        "ServiceOrderID" : 1,
        "Description": "Heat pump malfunction error code 415",
        "Priority": "High",
        "WorkCenter": "6575",
        "Operations":
        [
            {
                "OperationID" : 1,
                "ServiceOrderID" : 1,
                "Critical" : true,
                "Description": "Clean exterior filter"
            },
            {
                "OperationID" : 2,
                "ServiceOrderID" : 1,
                "Critical" : true,
                "Description": "Check freon pressure"
            }
        ]
    }
]

Loading Test Data

Provisioning test data for entity sets is optional.

When you create a database table, test data is automatically loaded from the web application archive (WAR file) and inserted into the database, as long as the TEST_MODE constant in the generated TestSettings.java file is true. By default, the value of the TEST_MODE constant is false, so you'll need to change it to true to enable the test data to load automatically. You'll want to do this only for development, test packaging, or deployment, and you should set the constant back to false before production packaging and deployment (as compared to initial data, which will be automatically loaded even in production deployments).

In the generated Maven project (in source code), the resource subfolder for test data is src/main/resources/test-data. For a compiled Java servlet-based application, the resource subfolder for test data is WEB-INF/classes/test-data.

Each test data file uses the name EntitySetName.json. For example, create the test data for a ServiceOrderSet entity in the subfolder of src/main/resources/test-data and call the file ServiceOrderSet.json. Once the project is compiled and is packaged into a Java EE WAR file using Maven, that file is located as WEB-INF/classes/test-data/ServiceOrderSet.json within the WAR file.

Example:

[
    {
        "ServiceOrderID" : 1,
        "Description": "Heat pump malfunction error code 415",
        "Priority": "High",
        "WorkCenter": "6575"
    },
    {
        "ServiceOrderID" : 2,
        "Description": "Furnace noisy",
        "Priority": "Medium",
        "WorkCenter": "6767"
    },
    {
        "ServiceOrderID" : 3,
        "Description": "Clean the ducts",
        "Priority": "Medium",
        "WorkCenter": "6767"
    }
]

Note

  • Key values in initial data are usually not reflected in the target database; they serve only to indicate relationships between entities in separate JSON files.
  • Test data is only inserted into the database if it is available in the WAR file when the server application is first started. Subsequently adding test data to your project and redeploying your OData service (without a version number change) will result in the test data not being inserted into the database.
  • Nesting entities in test data files works similarly to what is described in Nesting Child Entities for initial data files.

Last update: November 18, 2021