Platform Core

The Nemesis Platform core module holds the Spring Data repositories and services that interact with the JPA entities. This is the place where you define all your database-related source code. In this guide you may find information about:

Definitions

Definitions are the Nemesis way to define new and modify existing database entities. The Nemesis Platform uses standard JPAs to access the underlying database layer. At the same time, we need a way to define database entities. Of course, these entities can be defined as classes, but we also want to provide modularity. The aim is to have scenarios like this one covered:

If the user has moduleA and moduleB in his classpath, then the ProductEntity must have attributeA and attributeB (each module contributes one attribute). If, however, the user puts only moduleA in his classpath then the ProductEntity must have only attributeA.

— Petar Tahchiev

This, of course, will propagate down to the database columns as well. To achieve such kind of modularity, we can’t simply use classes that are defined in the platform. Instead, what we do is to introduce interfaces. From there on, each module specifies their own interface, containing the get/set methods of the attributes this module wants to contribute.

The idDiscriminator-s for the different modules, used in the Nemesis Platform, are defined in the table below:

1-50

Platform core

75-120

Commerce module

120-130

Barcode module

130-140

Blog module

140-150

Classification module

150-170

Cms module

170-180

Customer review module

180-190

Deeplink module

190-200

Gurantee module

200-210

Invoice module

210-220

Personalization module (ACL)

220-250

Promotion module

250-260

Reports module

260-270

Rule module

270-280

Sitemap module

280-290

Social module

290-300

Storelocator module

300-310

Targeting module

310-320

Voucher module

320-330

Wishlist module

330-340

Paypal module

340-360

Search module

360-400

Widgets

400-420

B2B Commerce

When adding a custom discriminator, be careful not to duplicate an existing one. Opt for a number equal or greater than 10 000 as a starting one.

Usage

To make it more clear let’s take a concrete example. As we already spoke about the ProductEntity, let’s examine it closer and see how to create a new definition:

io.nemesis.platform.module.commerce.core.definition.catalog.ProductEntityDefinition.java
Unresolved directive in <stdin> - include::/opt/bitnami/apps/jenkins/jenkins_home/workspace/nemesis-platform-master/nemesis-platform-core/../modules/nemesis-module-commerce/src/main/java/com/nemesis/platform/module/commerce/core/definition/catalog/ProductEntityDefinition.java[tags=class]
It’s a nice convention to put all your definitions in a package following this pattern: io.nemesis.platform.[core|module].[name-of-module].core.definition.[subpackages].

So a new ~Definition is simply an interface that:

1 has the @IdDiscriminator annotation;
2 extends AbstractEntityDefinition

The AbstractEntityDefinition is mandatory, because it provides all of the common functionalities for all entities in the Nemesis Platform (id, code, lastModifiedDate, lastModifiedAuthor, etc.).

If you do not specify the code property, a random UUID's hashcode will be generated. Furthermore, if the entity in question is nameable (i.e. - an instance of AbstractNameableEntityDefinition), then the code will be generated as a combination of the entitity’s slug and the UUID’s hashcode.
ex:your-product-name.6068324

The @IdDiscriminator annotation is also mandatory, as it tells us that this will end up being a new entity.

You can make your ~Definition extend other definitions, so that it will get additional attributes straight out of the box. In our case, the extended definitions are the following:

  • AbstractActivatableEntityDefinition - makes our entity activatable and adds three new attributes to our ProductEntity:

    • activeFrom (java.time.LocalDateTime),

    • activeTo (java.util.LocalDateTime),

    • active (java.lang.Boolean)

  • AbstractCatalogableEntityDefinition - makes our entity catalogable and adds two new attributes to our ProductEntity:

    • catalogVersion (CatalogVersionEntityDefinition) and

    • processed (java.lang.Boolean)

  • AbstractDescriptionableEntityDefinition:

    • description field added to the ProductEntity

  • AbstractNameableEntityDefinition:

    • name field added to the ProductEntity

So far so good - we have a definition now and we can use it in our services and repositories. However, what we want, is to create another module and when this module is in the classpath we want our ProductEntity to have one more attribute. When this module is not in the classpath we don’t want to have this new attribute. Let’s have a look at the customerreview module.

This module allows for customers to give ratings and reviews to different products. Considering this, it makes sense to enrich the ProductEntityDefinition with another set of attributes. For this purpose we create the ReviewedProductEntityDefinition:

io.nemesis.platform.module.customerreview.core.definition.ReviewedProductEntityDefinition.java
Unresolved directive in <stdin> - include::/opt/bitnami/apps/jenkins/jenkins_home/workspace/nemesis-platform-master/nemesis-platform-core/../modules/nemesis-module-customerreview/src/main/java/com/nemesis/platform/module/customerreview/core/definition/ReviewedProductEntityDefinition.java[tags=class]

As you can see, this ~Definition has, the @MixIn annotation. It specifies that we are not going creating a new definition, but rather enriching an already existing one (to read more for the @MixIn, refer to this guide). For that exact same purpose we are now also extending the ProductEntityDefinition and we can write all of our services using this new ReviewedProductEntityDefinition definition. Keep in mind that if the user is using the API in the Customerreview module, he must be having it in his classpath so it’s safe to assume they will have these new attributes on their ProductEntity.

Generation

That’s all good - we now have two definitions - one specifying a new JPA entity, and another one enriching that new entity with some new attributes. However, currently, the JPA does not work with interfaces, so we must somehow create a JPA class that will correspond to what we have in the classpath. Luckily, the Nemesis Platform, takes care for all of that. The Platform ships with an annotation processor that is triggered before compilation and this annotation processor will generate the JPA entities (this time classes) in the target/generated-sources/annotations.

Entities

To see the generated entities, all we have to do, as a client, is to simply run the following command in our project’s core module:

mvn clean install

This will create a JPA class that has the attributes and the mappings for those attributes based on what modules you have in your classpath. Just for a test, you might want to exclude the customerreview module from your classpath to see that the customerreview attributes are not generated in the product.

The generated JPA entities will be generated in a package folling this convention: io.nemesis.platform.[core|module].[name-of-module].core.entity.[subpackages].

Again, these classes are generated every time you run a compilation so you must not modify them.

Repositories

Having the JPA entities generated as classes, we can use them in our Spring Data repositories to perform CRUD operations. One other benefit of creating Spring Data repositories is the fact that if you have the Rest services module in your classpath, you can have these repositories exposed via REST and perform HTTP API calls on them - completely out of the box.

Following the convention from above we tend to put all of our repositories in a package, similar to this one: io.nemesis.platform.[core|module] .[name-of-module].core.repository.jpa.[subpackages]. This is actually mandatory for the repositories to work.

Services

We now have definitions and these definitions lead to JPA classes being generated. We use these JPA classes together with our Spring Data repositories to perform CRUD operations on the database. The last component in the core section are the services.

The services in the core module are the glue that makes this whole thing work. In each module, you will find services that you can use to perform certain operations. Back in our example with the customerreview module, we have a CustomerReviewService that provides some basic API to call when working with this module:

io.nemesis.platform.module.customerreview.core.service.CustomerReviewService.java
Unresolved directive in <stdin> - include::/opt/bitnami/apps/jenkins/jenkins_home/workspace/nemesis-platform-master/nemesis-platform-core/../modules/nemesis-module-customerreview/src/main/java/com/nemesis/platform/module/customerreview/core/service/CustomerReviewService.java[tags=class]

As you can see these services only know about definitions and repositories - nothing else. This is the contract by design. Also, every service will have a public static field, called NAME (both the interface and the default implementation specify such a field). We use these fields to register the @Bean with this name in the Spring ApplicationContext:

io.nemesis.platform.module.customerreview.config.CustomerReviewModuleAutoConfiguration.java
Unresolved directive in <stdin> - include::/opt/bitnami/apps/jenkins/jenkins_home/workspace/nemesis-platform-master/nemesis-platform-core/../modules/nemesis-module-customerreview/src/main/java/com/nemesis/platform/module/customerreview/config/CustomerReviewModuleAutoConfiguration.java[tags=bean]

The NAME field of the class is used as a bean name, and the NAME field of the interface is used as a bean alias. On this way, if you want to override some specific logic, you can define your own implementation of the interface, with your own NAME field, override the method, and then redeclare the bean with your NAME and the same alias.

Configuration

Name Type Description

nemesis.avatar.missing-image-url

java.lang.String

A url for an image to show when no avatar image is available.

nemesis.geocoder.api-key

java.lang.String

The client id for the geocoder that is used.

nemesis.geocoder.fallback-latitude

java.lang.Double

nemesis.geocoder.fallback-longitude

java.lang.Double

nemesis.media.base-path

java.lang.String

The base path to use for all media urls.

nemesis.media.base-path-prefix

java.lang.String

The base path prefix to use for all media urls.

nemesis.media.base-uri

java.lang.String

The base uri to use for all media urls.

nemesis.media.cache-control-value

java.lang.String

The Cache-Control header value. Defaults to public with a ten year period.

nemesis.media.expires-period-value

java.lang.Integer

The Expires header value to add to the current date. Value is in seconds, defaults to ten years.

nemesis.media.location

java.lang.String

Where the medias are located.

nemesis.platform.cache.enable-default-cache-region-configuration

java.lang.Boolean

Do you want to use the default cache region configuration for all the cache regions?

nemesis.platform.cache.expiry-policy-minutes

java.lang.Long

The cache configuration expiry policy in minutes.

nemesis.platform.cache.statistics-enabled

java.lang.Boolean

The cache configuration statisticsEnabled property.

nemesis.platform.cache.store-by-value

java.lang.Boolean

The cache configuration storeByValue property.

nemesis.platform.console-url

java.lang.String

The url where the backend console is deployed.

nemesis.platform.csv.enable-catalog-versions

java.lang.Boolean

Whether to import catalogable items in correct catalog version. If this property is set to false then every catalogable entity will be imported in Online catalog version during csv import.

nemesis.platform.csv.fail-on-error

java.lang.Boolean

Shall we fail the import process if an error occurs?

nemesis.platform.csv.folder

java.lang.String

The source folder where all the csv files reside.

nemesis.platform.csv.max-lines-per-row

java.lang.Integer

The maximum number of lines per row in a csv file.

nemesis.platform.csv.worker-threads

java.lang.Integer

The number of threads to perform csv import with.

nemesis.platform.data-sets

java.util.List<java.lang.String>

The name of the data set to import along with default csv files.

nemesis.platform.date-format

java.lang.String

The fallback date format to use across the platform.

nemesis.platform.debug

java.lang.Boolean

Is the platform started in debug mode? If FALSE then optimize performance.

nemesis.platform.default-currency

java.lang.String

The fallback default currency to use.

nemesis.platform.default-locale

java.util.Locale

The fallback default locale to use.

nemesis.platform.executor.pool-size

java.lang.Integer

Specifies the pool size of the executor service.

nemesis.platform.facade.rest.active

java.lang.Boolean

nemesis.platform.facade.rest.base-path

java.lang.String

nemesis.platform.facade.rest.cors.allow-credentials

java.lang.Boolean

nemesis.platform.facade.rest.cors.allowed-headers

java.util.List<java.lang.String>

nemesis.platform.facade.rest.cors.allowed-methods

java.util.List<java.lang.String>

nemesis.platform.facade.rest.cors.allowed-origins

java.util.List<java.lang.String>

nemesis.platform.facade.rest.cors.exposed-headers

java.util.List<java.lang.String>

nemesis.platform.facade.rest.cors.max-age

java.lang.Long

nemesis.platform.facade.rest.expose-root-resource

java.lang.Boolean

nemesis.platform.facade.rest.maximum-sessions

java.lang.Integer

nemesis.platform.facade.rest.swagger.enabled

java.lang.Boolean

Set to true if you want to enable the open api.

nemesis.platform.job.allow-parallel-execution

java.lang.Boolean

Do we want to allow multi job executions with the same parameters.

nemesis.platform.job.auto-schedule

java.lang.Boolean

Do we want to autoschedule all the beans of type {@code Job}.

nemesis.platform.job.task-executor.await-termination-seconds

java.lang.Integer

nemesis.platform.job.task-executor.core-pool-size

java.lang.Integer

nemesis.platform.job.task-executor.credentials

java.lang.String

nemesis.platform.job.task-executor.daemon

java.lang.Boolean

nemesis.platform.job.task-executor.max-pool-size

java.lang.Integer

nemesis.platform.job.task-executor.roles

java.lang.String[]

nemesis.platform.job.task-executor.username

java.lang.String

nemesis.platform.job.task-executor.wait-for-tasks-to-complete-on-shutdown

java.lang.Boolean

nemesis.platform.job.task-scheduler.pool-size

java.lang.Integer

The size of the pool for the {@code TaskScheduler}.

nemesis.platform.jwt.secret-key

java.lang.String

nemesis.platform.jwt.validity-in-milliseconds

java.lang.Long

nemesis.platform.media-import-folder

java.lang.String

Inspect this folder during initialization for medias to be uploaded to the final file storage destination.

nemesis.platform.migrate-database-on-startup

java.lang.Boolean

Set to true if you want to perform flyway migrate on startup. False otherwise

nemesis.platform.money-format

java.lang.String

The fallback money format to use across the platform.

nemesis.platform.offline

java.lang.Boolean

Is the platform in offline mode? All requests that access different resources on the internet will be stopped.

nemesis.platform.resource-folders

java.util.List<java.lang.String>

The path to the resource folder.

nemesis.platform.sync.chunk-size

java.lang.Integer

nemesis.platform.sync.isolation-level

java.lang.Integer

The isolation level of the transaction to use when synchronizing. The default value is 1 (TRANSACTION_READ_UNCOMMITTED). NOTE: Oracle database does not support read uncommitted transactions!

nemesis.platform.sync.worker-threads

java.lang.Integer

Specifies the amount of threads to use during catalog synchronization.

nemesis.platform.tenants.resource

org.springframework.core.io.Resource

The location of the tenants properties file.

nemesis.platform.time-format

java.lang.String

The fallback time format to use across the platform.

nemesis.platform.token-type

io.nemesis.platform.config.properties.NemesisPlatformProperties$TokenType

The type of token to use in the platform.

nemesis.redis.embedded

java.lang.Boolean

Should we start redis in embedded mode?

nemesis.redis.max-memory-size

java.lang.String

nemesis.schema.delimiter

java.lang.String

Specified the delimiter to use inside the generated SQL schema files.

nemesis.schema.format

java.lang.Boolean

Shall we format the generated SQL schema files?

nemesis.schema.location

java.lang.String

Specifies the location of the generated SQL schema files.

nemesis.schema.prefix

java.lang.String

Prefix for the generated schema files.

nemesis.security.http-utils.allow-empty-u-r-l

java.lang.Boolean

nemesis.security.http-utils.compiled-whitelist-patterns

java.util.List<java.util.regex.Pattern>

nemesis.security.http-utils.validator-enabled

java.lang.Boolean

nemesis.security.http-utils.whitelist-patterns

java.lang.String

nemesis.security.max-allowed-failed-login-attempts

java.lang.Integer

nemesis.server.site

java.util.Map<java.lang.String,io.nemesis.platform.config.properties.NemesisSite>

A map that holds site name and site parameter to use to open the given site.

nemesis.server.ssl.ciphers

java.lang.String[]

nemesis.server.ssl.client-auth

org.springframework.boot.web.server.Ssl$ClientAuth

nemesis.server.ssl.enabled

java.lang.Boolean

nemesis.server.ssl.enabled-protocols

java.lang.String[]

nemesis.server.ssl.key-alias

java.lang.String

nemesis.server.ssl.key-password

java.lang.String

nemesis.server.ssl.key-store

java.lang.String

nemesis.server.ssl.key-store-password

java.lang.String

nemesis.server.ssl.key-store-provider

java.lang.String

nemesis.server.ssl.key-store-type

java.lang.String

nemesis.server.ssl.port

java.lang.Integer

nemesis.server.ssl.protocol

java.lang.String

nemesis.server.ssl.trust-store

java.lang.String

nemesis.server.ssl.trust-store-password

java.lang.String

nemesis.server.ssl.trust-store-provider

java.lang.String

nemesis.server.ssl.trust-store-type

java.lang.String