SimpleApp

The quickest way to get started building an application "for real" is to use the simpleapp starter app.

As with the HelloWorld starter app, this will download a very simple one-class domain model, namely the SimpleObject entity and a supporting SimpleObjects domain service.

However, the app also provides more structure to assist you as your application grows, with multiple Maven modules, and examples of unit tests, integration tests and BDD (cucumber) specs.

Prerequisites

Apache Isis is a Java based framework, so in terms of prerequisites, you’ll need to install:

  • an LTS version of Java: either Java 8 JDK or Java 11 JDK

  • Apache Maven 3.5+

You’ll probably also want to use an IDE; the Apache Isis committers use either IntelliJ or Eclipse; in the Setup Guide we have detailed setup instructions for using these two IDEs. If you’re a NetBeans user you should have no problems as it too has strong support for Maven.

When building and running within an IDE, you’ll also need to configure the Datanucleus enhancer. This is implemented as a Maven plugin, so in the case of IntelliJ, it’s easy enough to run the enhancer as required. It should be just as straightforward for NetBeans too.

For Eclipse the maven integration story is a little less refined. All is not lost, however; DataNucleus also has an implementation of the enhancer as an Eclipse plugin, which works well enough.

Downloading & Running

Create a new directory, and cd into that directory.

To build the app from the latest stable release, then run the following command:

curl https://codeload.github.com/apache/isis-app-simpleapp/zip/2.0.0-M3 | jar xv
cd isis-app-simpleapp-2.0.0-M3

mvn clean install
mvn -pl webapp spring-boot:run

This should only take a few seconds to download, compile and run. Then browse to http://localhost:8080, and read on.

Using the App

When you start the app, you’ll be presented with a welcome page from which you can access the webapp using either the generic UI provided by Wicket viewer or use Swagger to access the Restful Objects viewer:

010 root page

Choose the generic Wicket UI, and navigate to the login page:

020 login to wicket viewer

The app itself is configured to run using shiro security, as configured in the WEB-INF/shiro.ini config file. You can login with:

  • username: sven

  • password: pass

Wicket viewer

Once you’ve logged in you’ll see the default home page:

030 home page

This is a view model annotated with @HomePage:

HomePageViewModel.java
@DomainObject(
        nature = Nature.VIEW_MODEL,
        objectType = "domainapp.HomePageViewModel"
        )
@HomePage
public class HomePageViewModel {

    public TranslatableString title() {
        return TranslatableString.tr("{num} objects", "num", getObjects().size());
    }

    public List<SimpleObject> getObjects() {
        return simpleObjects.listAll();
    }

    @Inject SimpleObjects simpleObjects;
}

Fixtures

When prototyping against an in-memory database, it’s useful to have a mechanism to create some initial state. This can be done using fixture scripts, accessible from the Prototyping  Run Fixture Script:

032 run fixture script prompt

This brings up a dialog to select the fixture script:

033 run fixture script prompt

The "Domain App Demo" in the drop-down refers to this code:

DomainAppDemo.java
public class DomainAppDemo extends FixtureScript {

    @Override
    protected void execute(final ExecutionContext ec) {
        ec.executeChild(this, new TeardownFixtureAbstract2() {
            @Override
            protected void execute(final ExecutionContext executionContext) {
                deleteFrom(SimpleObject.class);
            }
        });
        ec.executeChild(this, new SimpleObject_persona.PersistAll());
    }
}

When executed, all objects created by the fixture are listed:

034 run fixture script result

Navigating back to the home page shows the newly created objects:

036 home page after fixture scriptpng

As well as running in fixtures from the UI, you can also specify a configuration property to run a fixture script as part of the application start-up:

config/application.properties
# isis.testing.fixtures.initial-script = \
#    domainapp.webapp.application.fixture.scenarios.DomainAppDemo

Create an object

You can also of course create new objects:

040 create object from menu

To avoid obscuring the current page, this is configured to bring up a sidebar dialog:

050 create object from menu prompt

Hitting OK returns the created object:

060 created object

The above functionality is implemented by this code:

public static class CreateActionDomainEvent extends ActionDomainEvent {}
@Action(semantics = SemanticsOf.NON_IDEMPOTENT,
        domainEvent = CreateActionDomainEvent.class)                     (1)
@ActionLayout(promptStyle = PromptStyle.DIALOG_SIDEBAR)                  (2)
public SimpleObject create(
        @Name final String name) {                                       (3)
    return repositoryService.persist(SimpleObject.ofName(name));
}
1 Published on an in-memory event bus; this allows subscribers to veto or to react to the action invocation.
2 Policies such as modal or sidebar can be specified per action, or globally in application.yml file.
3 The @Name annotation is actually a meta-annotation, meaning that a number of additional semantics (length, layout, validation etc.) are inferred.

Invoke an action

The SimpleObject contains a couple of properties, and a single action to update that property.

The name property is read-only, and can only be modified using the updateName action:

070 update name

The above functionality is implemented by this code:

public static class UpdateNameActionDomainEvent
                      extends SimpleObject.ActionDomainEvent {}
@Action(semantics = IDEMPOTENT,
        publishing = Publishing.ENABLED,                    (1)
        associateWith = "name",
        domainEvent = UpdateNameActionDomainEvent.class)
public SimpleObject updateName(
        @Name final String name) {
    setName(name);
    return this;
}
public String default0UpdateName() {
    return getName();
}
1 Publishing enabled means that the action invocation is converted into XML and dispatched to all configured implementations of the PublisherService SPI.

The framework provides a default implementation that just logs the interaction:

075 publish logging 1

It also logs the fact that the object itself was modified:

076 publish logging 2

Edit a property

The notes property is editable, and can be edited in-place. For example:

080 edit notes

Actions requiring confirmations

It’s also possible to delete an object:

090 delete object

The viewer displays a message confirming that the object has been deleted:

100 object deleted

The above functionality is implemented by this code:

public static class DeleteActionDomainEvent
                       extends SimpleObject.ActionDomainEvent {}
@Action(semantics = NON_IDEMPOTENT_ARE_YOU_SURE,
        domainEvent = DeleteActionDomainEvent.class)
public void delete() {
    final String title = titleService.titleOf(this);                 (1)
    messageService.informUser(String.format("'%s' deleted", title));
    repositoryService.removeAndFlush(this);
}
1 Note that this method uses three services provided by the framework; these are injected into the domain object automatically.

Swagger (Restful Objects)

Using Prototyping  Open Swagger UI menu item (or just going back to the home page at localhost:8080) we can use Swagger UI as a front-end to the REST API provided by the Restful Objects viewer.

200 swagger ui before reload

The public API (where the calling client is assumed to be 3rd party) only exposes view models, not entities. If the API is private (or for prototyping), then resources corresponding to entities are also exposed:

210 simpleapp resources

For example, an object can be created using the resource that represents the SimpleObjects#create action:

220 create object thru rest api
if invoking the action returns a 401 (unauthorised), then navigate to the REST API directly (http://localhost:8080/restful to authenticate the browser first]).

The response indicates that the object was successfully created:

230 create object thru rest api response

The Swagger UI also provides a resource to retrieve any object:

240 retrieve object using rest api

This results in a representation of the domain object (as per the requested Response Content Type, ie ACCEPT header):

250 retrieve object using rest api response

The Swagger UI is provided as a convenience; the REST API is actually a complete hypermedia API (in other words you can follow the links to access all the behaviour exposed in the regular Wicket app). The REST API implemented by Apache Isis is specified in the Restful Object spec.

Structure of the App

The simpleapp starter app is a multi-module project, structured so that you easily extend it as your application grows.

Application Modules

The application consists of three modules, with a top-level module acting as an aggregator, the module-simple module that contains the business logic, and webapp module that acts as a bootstrapper.

diagram
Figure 1. simpleapp module dependencies

Each Maven module contains exactly one @Configuration class, residing at the package root for that Maven module. So, the module-simple maven The location of this class is used for classpath scanning.

In a larger application there would likely be many more modules containing these domain object modules. For example, you might have a module-customer holding a Customer entity and related entities/services, a module-product holding a Product catalog, and a module-order to hold the Orders placed by Customers:

diagram
Figure 2. Typical application dependencies

We can use Maven dependency management to ensure that there are no cyclic dependencies (order "knows about" product but product does not know about orders) and ensure that the codebase remains decoupled. If Java9 modules become commonplace, we’ll also be able to restrict visibility of classes between modules.

Bootstrapping & Framework Modules

One of the main responsibilities of Spring Boot is - naturally enough - to bootstrap the application. For the webapp, this is done using a class annotated with @SpringBootApplication. For integration tests, this uses the @SpringBootTest annotation.

These two different annotations reference a (class annotated with) @Configuration, which in turn can @Import other annotations.

By convention, the top-level @Configuration in an Apache Isis application is called the "app manifest". This references not only the application-level modules (described above), but also the framework’s modules. There will be several of these app manifests, typically one for the webapp itself, and one for integration testing each module.

The diagram below shows how classes fit together:

simpleapp modules dependencies
Figure 3. Module Dependencies

Let’s now review the contents of each of the modules in the simpleapp starter app.

module-simple’s src/main/java

Under src/main/java we have:

src/main/java/
  domainapp/                            (1)
    modules/
      simple/                           (2)
        dom/                            (3)
          so/                           (4)
            SimpleObject.java
            SimpleObject.layout.xml
            SimpleObject.png
            SimpleObjects.java
        fixture/                        (5)
          SimpleObject_persona.java
          SimpleObjectBuilder.java
        types/                          (6)
          Name.java
          Notes.java
      SimpleModule.java                 (7)
  META-INF/
    persistence.xml                     (8)
1 For simplicity, all the Java source files reside in a domainapp top-level package. Change as required.
2 Defines the 'simple' module. Apache Isis can be used for both microservice and monolithic architectures, but for the latter it encourages "modular monoliths" from the start.
3 The dom package holds the "domain object model" for this module. Modules may have other packages, common ones include types and fixtures (as below), also apis, contributions, spis
4 Holds classes for the so ("simple object") entity/aggregate, consisting of the entity definition itself (SimpleObject) and a corresponding repository (SimpleObjects). The associated .layout.xml and .png are optional but provide metadata/resources for rendering (Maven is configured to also treat src/main/java as a resource location).
5 The fixture package contains classes to set up the database to an initial state (as described earlier. One of the classes is a FixtureScript (defines how to set up the data) the other is a persona enum (defines what data to set up).
6 The types package contains meta-annotations to describe the usage of common value types such as Strings.
7 SimpleModule is a Spring @Configuration which allows the domain services and entities of the module to be located.
This is discussed in more detail below.
8 The persistence.xml file is required when using the JDO/DataNucleus object store (though it is basically boilerplate, an empty file).

SimpleModule

Every module within an Apache Isis application should have a module class. Its purpose is to define a package to scan from, and optionally to declare any transitive dependencies. In the case of SimpleModule, it consists of:

SimpleModule.java
package domainapp.modules.simple;
... imports omitted ...
@Configuration
@Import({})                                                                 (1)
@ComponentScan                                                              (2)
public class SimpleModule
                implements ModuleWithFixtures {                             (3)

    @Override
    public FixtureScript getTeardownFixture() {
        return new TeardownFixtureAbstract2() {
            @Override
            protected void execute(ExecutionContext executionContext) {
                deleteFrom(SimpleObject.class);
            }
        };
    }

    public static class PropertyDomainEvent<S,T>                            (4)
    extends org.apache.isis.applib.events.domain.PropertyDomainEvent<S,T> {}

    public static class CollectionDomainEvent<S,T>
    extends org.apache.isis.applib.events.domain.CollectionDomainEvent<S,T> {}

    public static class ActionDomainEvent<S> extends
    org.apache.isis.applib.events.domain.ActionDomainEvent<S> {}

    @ConfigurationProperties("app.simple-module")                           (5)
    @Data
    @Validated
    public static class Configuration {
        private final Types types = new Types();
        @Data
        public static class Types {
            private final Name name = new Name();
            @Data
            public static class Name {
                private final Validation validation = new Validation();
                @Data
                public static class Validation {
                    private char[] prohibitedCharacters = "!&%$".toCharArray();
                    private String message = "Character '{}' is not allowed";
                }
            }
        }
    }

}
1 This module has no dependencies. If there were, these would be expressed in terms of module classes (each being a Spring @Configuration)
2 specifies this class' package as a root to scan for Spring @Components.
3 Optionally, modules can implement the testing’s ModuleWithFixtures interface. Through this, they can provide a fixture script which can be used to teardown all entities that are "owned" by the module. Since the module dependency graph is known, this allows all data to be removed, useful for prototyping and sometimes for integration tests.
4 The actions for the domain objects and domain services all emit domain events, ultimately inheriting from ActionDomainEvent. By convention, this inheritance hierarchy includes classes defined at the module level. This then allows subscribes to subscribe to all actions emitted by anything within the module.
Events can also be emitted for properties and collections.
5 Spring Boot type-safe configuration, as per @ConfigurationProperties annotation. This Spring Boot feature is used by the framework, but can equally easily be used by application code. The @Name.Specification class uses this configuration property.

The scanning mechanism is leveraged by Apache Isis to pick up three types of classes:

This will pick up the SimpleObjects repository because it is annotated with the framework’s @DomainService annotation; itself meta-annotated with @Component.

  • all domain services

    These are classes that are annotated with the framework’s @DomainService annotation. Because @DomainService is meta-annotated as a @Component, these are found automatically and are managed by Spring (ie instantiated, injected etc).

    Depending on their nature, domain services are used to build up the menu, or are available to call programmatically, eg repositories, or sometimes both.

    In the simpleapp starter app, the only domain service is SimpleObjects. This appears in the menu, and also acts as a repository for the SimpleObject entity.

  • all entities.

    These are entities that are annotated with both @DomainObject annotation and with @javax.jdo.annotations.PersistenceCapable. Because @DomainObject is meta-annotated as a @Component, these are found automatically by Spring. They are passed through to the JDO/DataNucleus object store, in order to create database mappings from the entities to relational tables.

    In the simpleapp starter app, the only entity is SimpleObject.

  • all fixture scripts

    These are classes that extend from the testing applib’s FixtureScript class, and are used to setup the database when running in prototype mode (against an in-memory database).

    As already discussed, the fixture package provides classes to create sample objects, while the SimpleModule provides a fixture script to tear down all data from the module.

module-simple’s src/test/java

Under src/test/java we have:

src/test/java/
  domainapp/
    modules/
      simple/
        dom/
          so/                                   (1)
            SimpleObject_Test.java
            SimpleObjects_Test.java
        integtests/
          tests/                                (2)
            SimpleObject_IntegTest.java
            SimpleObjects_IntegTest.java
          SimpleModuleIntegTestAbstract.java    (3)
1 These are very simple unit tests of SimpleObject and SimpleObjects with the package structure the same as in src/main/java. They are written in JUnit 5, and use JMockito as the mocking library.
2 Integration tests for the module. These use the WrapperFactory to simulate interactions through the UI.
3 The SimpleModuleIntegTestAbstract superclass bootstraps the module’s integration tests. This is discussed in more detail below.
these integration tests are annotated with the Spring @Transactional, which means that Spring will automatically roll back the changes to the database; there’s no need to delete data afterwards.
the naming convention — with Test and IntegTest suffixes — is important, because the Maven surefire plugin is configured to run multiple times, one <execution> for each suffix.

SimpleModuleIntegTestAbstract

The SimpleModuleIntegTestAbstract is the superclass of all integration tests in the module, annotated with @SpringBootTest:

SimpleModuleIntegTestAbstract.java
@SpringBootTest(
        classes = SimpleModuleIntegTestAbstract.AppManifest.class   (1)
)
@TestPropertySource({                                               (2)
        IsisPresets.H2InMemory_withUniqueSchema,                    (3)
        IsisPresets.DataNucleusAutoCreate,                          (4)
        IsisPresets.UseLog4j2Test,                                  (5)
})
public abstract class SimpleModuleIntegTestAbstract
        extends IsisIntegrationTestAbstractWithFixtures {           (6)

    @Configuration
    @Import({
        IsisModuleCoreRuntimeServices.class,                        (7)
        IsisModuleSecurityBypass.class,
        IsisModuleJdoDataNucleus5.class,
        IsisModuleTestingFixturesApplib.class,                      (8)

        SimpleModule.class
    })
    public static class AppManifest {
    }
}
1 The AppManifest (defined as a nested static class below) lists the modules needed to bootstrap the integration test.
2 Spring allows additional properties to be defined through the @TestPropertySource annotation
3 Runs the tests in memory using H2. A unique schema allows tests to potentially be run in parallel
4 Ensures that JDO/DataNucleus is configured to create the database schema first. This may be required because the application might otherwise be configured to use the Flyway integration.
5 Sets up logging to use the configuration defined in the log4j2-test.xml file
6 Tests typically inherit from IsisIntegrationTestAbstract, which provides some convenience methods to inherit from. In this case, the test inherits from the IsisIntegrationTestAbstractWithFixtures subclass which also adds in support for running fixtures.
7 Specifies the modules that make up Apache Isis framework itself. These include core, security set to the bypass implementation (effecively is ignored) and JDO/DataNucleus for persistence. Note that there no viewers are bootstrapped because the tests are run through Spring’s integration testing framework, rather than (say) as Selenium tests.
8 Brings in support for running fixtures.
You can learn more about testing in the Testing Guide.

webapp’s src/main/java

Under src/main/java we have:

src/main/java/
  domainapp/
    webapp/
      application/
        fixture/
          scenarios/                                            (1)
            DomainAppDemo.java                                  (2)
            DomainAppFixtureScriptsSpecificationProvider.java   (3)
        services/
          health/
            HealthCheckServiceImpl.java                         (4)
          homepage/
            HomePageViewModel.java                              (5)
            HomePageViewModel.layout.xml
            HomePageViewModel.png
         ApplicationModule.java                                 (6)
      AppManifest.java                                          (7)
      SimpleApp.java                                            (8)
1 Defines scenarios (fixtures) for setting up the system into a known state. Used for prototyping and also integration testing.
2 The DomainAppDemo is the fixture that was run earlier on.
3 The DomainAppFixtureScriptsSpecificationProvider is used to configure the run fixture script menu item shown on the "Prototyping" menu.
Fixture scenarios also need to be @Imported, typically in the top-level AppManifest.
4 An implementation of the HealtCheckService. This integrates with Spring Boot’s HealthIndicator SPI, surfaced through the Spring Boot Actuator.
5 Annotated with @HomePage and so is shown automatically as the home page.
6 Defines the services in the webapp module, along with dependencies onto the other modules in the application.
Discussed in more detail below.
7 AppManifest is the top-level Spring @Configuration that specifies the components of Apache Isis to use, along with the modules making up the application itself (ie ApplicationModule, which depends in turn on SimpleModule).
This is discussed in more detail below
8 SimpleApp is the @SpringBootApplication used to bootstrap the app. It’s pretty much boilerplate - the important piece is that it references AppManifest.
It’s discussed in more detail below

ApplicationModule

The ApplicationModule defines the services in the webapp module, along with dependencies onto the other modules in the application. It’s very simple though:

ApplicationModule.java
package domainapp.webapp.application;
... imports omitted ...
@Configuration
@Import(SimpleModule.class)             (1)
@ComponentScan                          (2)
public class ApplicationModule {
}
1 This module depends on the SimpleModule module.
2 specifies this class' package as a root to scan for Spring @Components.

AppManifest

The "app manifest" (the name has been retained from Apache Isis v1.x) is the top-level Spring @Configuration. It looks like this:

AppManifest.java
@Configuration
@Import({
        IsisModuleCoreRuntimeServices.class,                    (1)
        IsisModuleSecurityShiro.class,                          (2)
        IsisModuleJdoDataNucleus5.class,                        (3)
        IsisModuleViewerRestfulObjectsJaxrsResteasy4.class,     (4)
        IsisModuleViewerWicketViewer.class,                     (5)

        IsisModuleTestingH2ConsoleUi.class,                     (6)
        IsisModuleTestingFixturesApplib.class,                  (7)

        IsisModuleExtFlywayImpl.class,                          (8)

        ApplicationModule.class,                                (9)

        DomainAppDemo.class                                     (10)
})
@PropertySource(IsisPresets.NoTranslations)                     (11)
@PropertySource(IsisPresets.DebugDiscovery),
public class AppManifest {
}
1 Mandatory - specifies the core of the Apache Isis framework
2 Enables the Shiro security mechanism. There are several security implementations, precisely one must be selected
3 Enables JDO/DataNucleus for persistence. Optional (though if omitted then only view models may be used, with hand-rolled persistence).
4 Enables the Restful Objects viewer (ie REST API).
5 Enables the Wicket viewer
6 Enables the H2 Console (menu from the "Prototyping" menu), applicable only if running against h2 in-memory database.
7 Brings in support to run fixtures within the application, eg for prototyping.
8 Enables the Flyway integration.
9 References the application’s module(s), in this case just the one, ApplicationModule.
This is discussed below.
10 Makes the fixture scenario classes available for discovery.
11 Normally configuration properties are picked up from Spring Boot’s application.properties or application.yml files, but additional properties can be overridden directly. These two both use the IsisPresets convenience class, to disable i18n support and for additional debugging log statements.

SimpleApp

The application is bootstrapped using SimpleApp, a regular @SpringBootApplication. It is mostly boilerplate:

@SpringBootApplication
@Import({
    AppManifest.class,                                          (1)
})
public class SimpleApp
            extends SpringBootServletInitializer {

    public static void main(String[] args) {
        IsisPresets.prototyping();                              (2)
        SpringApplication.run(
                new Class[] { SimpleApp.class }, args);
    }
}
1 references the AppManifest mentioned earlier
2 specifies prototyping mode. This enables actions marked for prototyping to become available, useful during the early stages of development.
As an alternative to making this call, you can also just run with a system property -DPROTOTYPING=true

webapp’s src/main/resources

Under src/main/resources we have:

src/main/resources
  config/
    application.properties              (1)
    application-SQLSERVER.properties    (2)
  db/
    migration/
      SQLSERVER/
        Vyyyy.mm.dd.hh.ss__xxx.sql      (3)
  static/                               (4)
  templates/                            (5)
  application.yml                       (6)
  banner.txt                            (7)
  log4j2-spring.xml                     (8)
  menubars.layout.xml                   (9)
  shiro.ini                             (10)
1 By convention, we use config/application.properties to hold configuration properties that change between environments (dev, test, prod). Typically this just holds JDBC connection strings, etc.
2 Enabled if run using the SQLSERVER profile (eg using -Dspring.profiles.active=SQLSERVER). Configures to use a SQL Server database, and enables Flyway to set up the database schema
3 Flyway migration scripts.
This leverages a feature in Spring Boot’s Flyway integration which allows different variants of scripts for different vendors to be stored, in a db.migration.{vendor} package.
4 The static package (a Spring convention) provides access for static resources that are accessible from the webapp
5 The templates package holds a fallback error page, which is the default location for pages rendered using Spring Boot’s integration with Thymeleaf.
6 By convention, we use application.yml to hold configuration properties that do not change between environments.
7 The banner.txt is shown when bootstrapping.
8 The log4j2-spring.xml configures log4j2 (the logging system used by Apache Isis)
9 The menubars.layout.xml arranges the actions of the domain services into menus.
10 The shiro.ini file configures Shiro security integration (see the IsisModuleSecurityShiro module imported in the AppManifest, above).

The Shiro security integration is much more flexible than simple file-based authentication.

To call out some of the files under static:

  • The index.html is the page shown at the root of the package, providing links to either the Wicket viewer or to the Swagger UI. In a production application this is usually replaced with a page that does an HTTP 302 redirect to the Wicket viewer.

  • In css/application.css you can use to customise CSS, typically to highlight certain fields or states. The pages generated by the Wicket viewer have plenty of CSS classes to target. You can also implement the cssClass() method in each domain object to provide additional CSS classes to target.

  • Similarly, in scripts/application.js you have the option to add arbitrary Javascript. JQuery is available by default.

No src/main/webapp

Note that there is no src/main/webapp/ or WEB-INF/web.xml - the servlets and filters are configured by Apache Isis automatically.

webapp’s src/test/java

Under src/test/java we have different sets of tests. We’ll inspect each in turn.

the naming convention — with Test, IntegTest and IntegBddSpecs suffixes — is important, because the Maven surefire plugin is configured to run multiple times, one <execution> for each suffix.

BDD Specs

The simplapp starter app includes support for BDD (Cucumber) specifications. These reside under domain.webapp.bdd:

src/test/java
  domainapp/
    webapp/
      bdd/
        glue/
          SimpleObjectsStepDef.java                     (1)
        specs/
          SimpleObjectSpec_listAllAndCreate.feature     (2)
        RunIntegBddSpecs.java                           (3)
1 defines the step definitions (or "glue") which describes how to interact with the application.
This class inherits from ApplicationIntegTestAbstract, the base class for integration tests, described in the next section.
2 the feature file, discussed below.
3 boilerplate used to run the BDD Cucumber tests using JUni5 4.x.

There is just one feature file: SimpleObjectSpec_listAllAndCreate.feature, which is pretty simple:

Feature: List and Create New Simple Objects

  Scenario: Existing simple objects can be listed and new ones created
    Given there are initially 3 simple objects
    When  I create a new simple object
    Then  there are 4 simple objects

The set up of the 3 initial objects is part of the step definitions.

Integration Tests

The integration tests are in domainapp.application.integtests:

src/
  test/
    java/
      domainapp/
        webapp/
          integtests/
            metamodel/
              lockdown/
                approved/
                  ...
                current/
                  ...
                LockDownMetaModel_IntegTest.java    (1)
              ValidateDomainModel_IntegTest.java    (2)
            smoke/
              Smoke_IntegTest.java                  (3)
            ApplicationIntegTestAbstract.java       (4)
1 Uses approval tests to verify that the metamodel is unchanged between releases of Apache Isis.
Discussed in more detail below.
2 Bootstraps the app and runs the metamodel validators to check that there are not metamodel errors.
This can also be done simply when running the application, but performing the checks through integration tests enables "fail-fast" checking, as part of CI, for example.
3 Performs a number of high-level smoke tests, to check that the core functionality works correctly.
4 Base class used to bootstrap all integration tests for this module. It is very similar to the base class used to bootstrap the integration tests for the simple module about.adoc#SimpleModuleIntegTestAbstract, but referencing ApplicationModule rather than SimpleModule.

The purpose of the metamodel lockdown tests is to capture regressions in Apache Isis itself whenever it is updated.

Normally the metamodel of the application will evolve all of the time, so the metamodel lockdown tests are disabled by default and only enabled with -Dmetamodel.lockdown. If you want to capture the current metamodel, then enable and they will likely fail. The new export of the metamodel will be in current and can simply be approved (by copying over to the approved directory.

However, if you are upgrading Apache Isis then you may want to check that the metamodel being built by the next version of Apache Isis is the same as previous. In this case, update Apache Isis (by updating the `<version> of the parent pom) and run the lockdown tests to check for differences.

Unit Tests

The integration tests are in domainapp.application.unittests:

src/test/java
  domainapp/
    webapp/
       unittests/
         mavendeps/
           lockdown/
             approved/
               ...
             current/
               ...
             LockDownMavenDeps_Test.java                (1)
       util/
         CurrentVsApprovedApprovalTextWriter.java       (2)
1 lock down test to check the transitive set of dependencies used by the application
2 utility class used by the lock down tests (unit tests and integ tests).

The maven dependency lock down tests are disabled by default, only enabled using -Dmavendeps.lockdown. The pom.xml is configured to run the Maven dependency:tree and dependency:list goals, capturing the current dependencies. These are compared to the approved versions.

Any time the Apache Isis version is updated, run these tests using -Dmavendeps.lockdown to capture and approve the new versions.

The Maven dependency lock down tests are useful to capture any accidental changes to the dependency tree, so consider enabling them all of the time.

Root module’s pom.xml

In the parent module we of course have the pom.xml. This inherits from isis-app-starter-parent:

pom.xml
<parent>
    <groupId>org.apache.isis.app</groupId>
    <artifactId>isis-app-starter-parent</artifactId>
    <version>XXX</version>
</parent>

... which builds upon Spring Boot’s own org.springframework.boot:spring-boot-starter-parent. This means:

  • the set of third party dependencies declared have been validated as compatible by Spring Boot team

  • build plugins are declared and configured appropriately

  • imports to the Apache Isis dependencies are declared via <dependencyManagement>

Running from within the IDE

Most of the time you’ll probably want to run the app from within your IDE. The mechanics of doing this will vary by IDE; see the Setup Guide for details. Basically, though, it amounts to running the main() method in the SimpleApp, but also (and this bit is important) ensuring that the DataNucleus enhancer has properly processed all domain entities.

Here’s what the setup looks like in IntelliJ IDEA:

simpleapp webapp

which uses an IntelliJ feature to run a different run configuration to run DataNucleus enhancer beforehand:

simpleapp webapp before launch

Experimenting with the App

Once you are familiar with the app, try modifying it. There is plenty more guidance on this site; start with the User Guide Fundamentals and then look at the other guides linked to from the top-level menu or from the main table of contents.

If you run into issues, please don’t hesitate to ask for help on the users mailing list or the Slack channel, as per the support page.