4.1. Prerequisites
Apache Isis is a Java based framework, so in terms of prerequisites, you’ll need to install:
You’ll probably also want to use an IDE; the Apache Isis committers use either IntelliJ or Eclipse; in the Developers' 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 usually works well enough.
4.3. Structure of the App
As noted above, the application generated by the helloworld archetype is deliberately simplified, with everything contained within a single Maven module.
Under src/main/java
we have:
domainapp/
├── application/
│ ├── HelloWorldAppManifest.java
│ └── isis.properties
├── dom/
│ ├── HelloWorldModule.java
│ └── impl/
│ ├── HelloWorldObject.java
│ ├── HelloWorldObject.layout.xml
│ ├── HelloWorldObject.png
│ └── HelloWorldObjects.java
└── app/
├── HelloWorldApplication.java
└── welcome.html
META-INF/
└── persistence.xml
For simplicity, all the Java source files generated by the archetype are placed in a domainapp
top-level package.
|
While it’s more conventional to use the inverse domain name for package (eg com.mycompany.myapp , that’s only really appropriate for library code that will be released for reuse by multiple applications in different orgnisations (eg open source).
For internal application though this is less of a concern; indeed, avoiding using the domain name means that if the company rebrands or is taken over then nothing needs be changed.
Of course, you are always free to move the classes to a different package if you wish.
|
In domainapp.application
package is HelloWorldAppManifest
, which implements the AppManifest
interface defined by the Apache Isis applib. The class is only small, so is worth showing here in its entirety:
public class HelloWorldAppManifest extends AppManifestAbstract {
public static final Builder BUILDER = Builder
.forModules(HelloWorldModule.class)
.withConfigurationPropertiesFile(HelloWorldAppManifest.class, "isis.properties")
.withAuthMechanism("shiro");
public HelloWorldAppManifest() {
super(BUILDER);
}
}
Rather than implement AppManifest
directly, HelloWorldAppManifest
uses the builder provided by the convenience AppManifestAbstract
); as you’ll find when you work on bigger applications, it’s common to have variations around the app manifest, so the builder makes it easy to create these variations without lots of boilerplate.
For now, we see that (using the builder) the app manifest defines three things:
-
a list of modules - there’s just one, HelloWorldModule
for this class
-
the location of a configuration file, isis.properties
, read in as a resource from the classpath
-
specifying an authentication/authorisation mechanism, in this case Apache Shiro integration.
In Apache Isis a module is simply an empty class, which is used simply to obtain a package name. The framework uses classpath scanning to find certain classes; rather than scan the entire classpath it limits its search to the package(s) obtained from the modules.
The packages are scanned to identify three types of classes:
-
all entities.
These are entities that are annotated with @javax.jdo.annotations.PersistenceCapable
. These are passed through to the JDO (DataNucleus) object store, in order to create database mappings from the entities to relational tables
In the helloworld application, the only entity is HelloWorldObject
.
-
all domain services
These are classes that are annotated with the framework’s @DomainService
annotation. Depending on their nature, services are used to build up the menu, or are available to call programmatically, eg repositories. The framework instantiates an instance of each and will automatically inject the services into other domain objects and services.
In the helloworld application, the only domain service is HelloWorldObjects
. This appears in the menu, and also acts as a repository for the HelloWorldObject
entity.
-
all fixture scripts
These are classes that extend from the applib FixtureScript
class, and are used to setup the database when running in prototype mode (against an in-memory database).
The helloworld application doesn’t provide any examples of these.
The app manifest also identifies the isis.properties
file (in the same package as HelloWorldAppManifest
) as containing various configuration options. The helloworld application uses these for settings that are unlikely to change and so are loaded as a static resource from the classpath.
You’ll find that there’s another file called isis.properties
that’s in WEB-INF/isis.properties
. This also provides configuration options (the framework simply combines them) but those in WEB-INF/isis.properties
are restricted to settings that are likely to change from environment to environment, most notably JDBC URL connection strings. Separating these out makes it easy to reconfigure the application to run against different databases in different environments (dev, test, production etc).
Finally, the app manifest identifies Apache Shiro for authentication and authorisation. Shiro in turn is configured using the WEB-INF/shiro.ini
file.
|
The security integration provided by Apache Isis and Shiro is quite sophisticated; to get started though you can just login using username: sven , password: pass .
|
In the domainapp.dom
module ("dom" stands for "domain object model") is the HelloWorldModule
:
package domainapp.dom;
public final class HelloWorldModule {
private HelloWorldModule(){}
}
As already explained, this is simply used by the app manifest to identify "domainapp.dom" as the package to scan to locate entities (HelloWorldObject
), services (HelloWorldObjects
) and fixture scripts (none provided).
In the domainapp.dom.impl
we have the classes that actually comprise our domain object. These are a little large to list here in their entirety, but it’s worth calling out:
-
HelloWorldObject
:
-
is annotated with a bunch of JDO annotations, @PersistenceCapable
being the most important.
This annotation is what identifies the class as an entity, so that Apache Isis passes through to JDO/DataNucleus during bootstrapping (to create the ORM mappings).
-
is also annotated with Isis' own @DomainObject
.
This isn’t mandatory, but since entities are the real building blocks on which Isis applications are built, it’s very common to use this annotation.
-
also uses various Project Lombok annotations to remove boilerplate.
-
HelloWorldObject.layout.xml
defines the layout of the members (properties and actions) of the HelloWorldObject
.
Layout files are optional - Apache Isis is an implementation of the naked objects pattern, after all - but in most cases you’ll find it easiest to use one
-
HelloWorldObject.png
is used as an icon for any instances of the domain object shown in the (Wicket) viewer
-
HelloWorldObjects
is a domain service by virtue of the fact that it is annotated with Isis' @DomainService
.
This acts as both a menu (the @DomainService#nature
attribute) and also a repository to find/create instances of HelloWorldObject
.
The META-INF/persistence.xml
also relates to domain entities. JDO/DataNucleus allows metadata mapping information to be specified using either XML or annotations. In the Apache Isis community we generally prefer to use annotations, but nevertheless this file is required, even though it is basically empty:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="helloworld"/>
</persistence>
Finally, in the domainapp.webapp
we have HelloWorldApplication
. This is required to bootstrap the Wicket viewer (it is configured in WEB-INF/web.xml
). Internally it uses Google Guice to configure various static resources served up by Wicket:
public class HelloWorldApplication extends IsisWicketApplication {
...
protected Module newIsisWicketModule() {
final Module isisDefaults = super.newIsisWicketModule();
final Module overrides = new AbstractModule() {
@Override
protected void configure() {
...
}
};
return Modules.override(isisDefaults).with(overrides);
}
}
The configure()
method is the place to change the name of the application for example, or to change the initial about and welcome messages. The text of the welcome page shown by the Wicket viewer can be found in welcome.html
, loaded from the classpath and in the same package as HelloWorldApplication
.
Under src/main/webapp
we have various resources that are used to configure the webapp, or that are served up by the running webapp:
about/
└── index.html
css/
└── application.css
scripts/
└── application.js
swagger-ui/
WEB-INF/
├── isis.properties
├── logging.properties
├── shiro.ini
└── web.xml
Most important of these is WEB-INF/web.xml
, which bootstraps both the Wicket viewer and the Restful Objects viewer (the REST API derived from the domain object model).
The about/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.
In swagger-ui
is a copy of the Swagger 2.x UI classes, preconfigured to run against the REST API exposed by the Restful Objects viewer. This can be useful for developing custom applications, and is accessible from the initial page (served up by about/index.html
).
Finally in WEB-INF
we have the standard web.xml
(already briefly discussed) along with several other files:
-
isis.properties
contains further configuration settings for Apache Isis itself.
(As already discussed), these are in addition to the configuration properties found in the isis.properties
that lives alongside and that is loaded by the HelloWorldAppManifest
class. Those in the WEB-INF/isis.properties file are those that are likely to change when running the application in different environments.
-
logging.properties
configures log4j.
The framework is configured to use slf4j running against log4j.
-
shiro.ini
configures Apache Shiro, used for security (authentication and authorisation)
-
web.xml
configures the Wicket viewer and Restful Objects viewer. It also sets up various filters for serving up static resources with caching HTTP headers.
Under src/test/java
we have:
domainapp/
└── dom/
└── impl/
├── HelloWorldObjectTest_delete.java
└── HelloWorldObjectTest_updateName.java
These are very simple unit tests of HelloWorldObject
. They use JMock as the mocking library (with some minor extensions provided by Apache Isis itself).
Finally, at the root directory we of course have the pom.xml
. Some notable points:
-
since this module generates a WAR file, the pom.xml
uses <packaging>war</packaging>
-
maven mixins are used to remove boilerplate configuration of standard plugins (resources, compile, jar etc), for the DataNucleus enhancer, for surefire (tests), and for running the application using jetty plugin
Now you know your way around the code generated by the archetype, lets see how to build the app and run it.
4.6. Using the App
The Wicket viewer provides a human usable web UI (implemented, as you might have guessed from its name, using Apache Wicket), so choose that and navigate to the login page:
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
Once you’ve logged in you’ll see the default home page:
The application is configured to run with an in-memory database, so initially there is no data. Create an object using the menu:
which brings up a modal dialog:
hitting OK returns the created object:
The above functionality is implemented by this code:
@Action(semantics = SemanticsOf.NON_IDEMPOTENT)
@MemberOrder(sequence = "1")
public HelloWorldObject create(
@Parameter(maxLength = 40)
@ParameterLayout(named = "Name")
final String name) {
final HelloWorldObject object = new HelloWorldObject(name);
serviceRegistry.injectServicesInto(object);
repositoryService.persist(object);
return object;
}
The HelloWorldObject
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.
The above functionality is implemented by this code:
@Action(
semantics = SemanticsOf.IDEMPOTENT,
command = CommandReification.ENABLED,
publishing = Publishing.ENABLED
)
public HelloWorldObject updateName(
@Parameter(maxLength = 40)
@ParameterLayout(named = "Name")
final String name) {
setName(name);
return this;
}
-
The notes
property is editable, and can be edited in-place.
It’s also possible to delete an object:
The viewer displays a message confirming that the object has been deleted:
The above functionality is implemented by this code:
@Action(semantics = SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE)
public void delete() {
final String title = titleService.titleOf(this);
messageService.informUser(String.format("'%s' deleted", title));
repositoryService.removeAndFlush(this);
}
This uses three services provided by the framework; these are injected into the domain object automatically.
Going back to the home page (localhost:8080) we can use Swagger UI as a front-end to the REST API provided by the Restful Objects viewer.
The Swagger UI is created dynamically from a Swagger schema definition (the schema definition file itself can be downloaded from the Wicket viewer’s "Prototyping" menu). This Swagger schema definition groups resources according to Apache Isis metadata:
For example, an object can be created using the resource that represents the HelloWorldObjects#create
action:
The response indicates that the object was successfully created:
The Swagger UI also provides a resource to retrieve any object:
This results in a representation of the domain object (as per the requested Response Content Type
, ie ACCEPT
header):
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.